我的网站:欢迎欢迎
JDBC进阶
PreparedStatement
预处理语句,这个接口继承了Statement接口
1.解决字符串拼接的问题
2.解决sql注入问题
3.让效率更高一点
实现步骤
前面连接数据库都是一样的步骤
在拿到语句对象的时候就使用SQL
将Statement st = conn.createStatement();
替换成PreparedStatement st = conn.prepareStatement(sql);//预编译sql
在执行的时候不需要传sql;
String sql = "select * from login where username=? and password=?";
PreparedStatement pStatement = conn.prepareStatement(sql);
pStatement.setString(1, username);
pStatement.setString(2, pwd);
ResultSet set = pStatement.executeQuery();
Demo
使用两种方式来判断登录
package com.ifueen.homework.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.ifueen.classtest.util.JDBCUtil;
import com.ifueen.classtest.util.JDBCUtilDataSource;
import com.ifueen.homework.dao.LoginDao;
import com.sun.javafx.binding.Logging;
public class LoginDaoImpl implements LoginDao{
/**
* 第一种判断登录的方式
*/
@Override
public void login(String username, String pwd) {
// TODO Auto-generated method stub
Connection conn = JDBCUtilDataSource.getinstance().getconn();
try {
Statement statement = conn.createStatement();
String sql = "select * from login";
ResultSet rs = statement.executeQuery(sql);
if (rs.next()) {
if (!rs.getString("username").equals(username)) {
System.out.println("你有这个账户吗?嗯?弟弟");
}else{
if (rs.getString("username").equals(username) && rs.getString("password").equals(pwd)) {
System.out.println("登录成功");
}else{
System.out.println("密码都记不住吗弟弟?");
}
}
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 第二种
* 使用prepareStatement来进行加载,避免了sql注入问题
*/
@Override
public void login2(String username, String pwd) {
// TODO Auto-generated method stub
Connection conn = JDBCUtilDataSource.getinstance().getconn();
try {
String sql = "select * from login where username=? and password=?";
PreparedStatement pStatement = conn.prepareStatement(sql);
pStatement.setString(1, username);
pStatement.setString(2, pwd);
ResultSet set = pStatement.executeQuery();
if (set.next()) {
if (set.getString("username").equals(username) && set.getString("password").equals(pwd))
System.out.println("登录成功");
}else{
System.out.println("密码都记不住吗弟弟?");
}
JDBCUtil.getinstance().getclose(pStatement, conn);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
?事务
什么是事务
事务: 程序里面的一组操作,要不都成功,要不都失败;
事务示例:
银行转帐功能: bank / money
小冯和小红:
小冯 : 10000块钱 小红 : 0块钱
转账:小冯要给小红转1000块钱
分析:转钱需要提供两条sql,但是程序员也会出错,比较代码写错了.
如果在扣款时出错,那么小冯的余额扣除了但是小红却没有收到,所以不合理
这个时候就需要用到事务操作了
事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(commit),这些修改就永久地保存下来,如果回退(rollback),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。
事物的四大特性(面试题)
事物四大特性:ACID
原子性:一组操作不可分割
一致性:操作前后需要一直
隔离性:并发编程的时候,多个线程之间的操作不会相会影响
持久性:将内存中的数据持久化到磁盘
Demo
利用事务实现的转账功能
package com.ifueen.homework.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.ifueen.classtest.util.JDBCUtilDataSource;
import com.ifueen.homework.dao.Transfer;
import com.sun.org.apache.bcel.internal.generic.Select;
public class TransferDaoImpl implements Transfer{
/**
* 查询账户余额
*/
@Override
public double select(String name) {
Double b = null;
// TODO Auto-generated method stub
Connection conn = JDBCUtilDataSource.getinstance().getconn();
String sql = "select balance from transfer where name = ?";
try {
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, name);
ResultSet rs = ps.executeQuery();
while(rs.next()){
b = rs.getDouble("balance");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return b;
}
/**
* 转账功能
*/
@Override
public void transfer(String name, String name2, double money) {
// TODO Auto-generated method stub
Connection conn = JDBCUtilDataSource.getinstance().getconn();
try {
//设置事务的自动提交为false,即不自动提交
conn.setAutoCommit(false);
String sql = "update transfer set balance= ? where name = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setDouble(1, select(name)-money);
ps.setString(2, name);
ps.executeUpdate();
//测试意外情况
//System.out.println(1/0);
//另一个账户增加
String sql1 = "update transfer set balance= ? where name = ?";
PreparedStatement ps1 = conn.prepareStatement(sql1);
ps1.setDouble(1, select(name2)+money);
ps1.setString(2, name2);
ps1.executeUpdate();
//完成操作提交事务
conn.commit();
JDBCUtilDataSource.getinstance().getclose(ps,conn);
JDBCUtilDataSource.getinstance().getclose(ps1,conn);
System.out.println("转账成功啦");
} catch (SQLException e) {
// TODO Auto-generated catch block
try {
//设置事务的自动回滚
conn.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}
}
}
注意!MySQL中,InnoDB支持外键.支持事务,MyISAM不支持外键,不支持事务
使用JDBC拿到主键
为什么需要拿到Id?
现在我们插入的一条数据,但是并不知道该数据的id是多少, 而我们有时候操作,需要这个id.
比如我们向product表插入一条数据,它的数量为200,但是product表里面并没有表示数量的字段,而product_store表里面含有storeNum,所有我们应该在插入数据之后,同时需要插入一条数据到product_store表里面; – 这时候我们就需要拿到product表里新插入的id;
拿到主键的方法:
Connection conn = JDBCUtilDataSource.getinstance().getconn();
String sql = "insert into student (name,age) value (?,?)";
PreparedStatement ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
ps.setString(1, name);
ps.setInt(2, age);
ps.execute();
//拿到主键的集合
ResultSet rs = ps.getGeneratedKeys();
while (rs.next()) {
System.out.println(rs.getLong(1));
}
连接池
为什么要连接池?
每次请求都会创建一个connection,因此会浪费资源(内存),当同时1000人访问的时候,那就会占用很多资源,因此很浪费时间和容器操作系统崩溃
认识连接池
节约内存资源 维护连接对象的生命周期
最开始的时候就创建一些连接对象放到连接池中 请求来了可以直接在连接池中获取连接 操作数据库
操作完成以后 释放连接 回到连接池 等待下一次被请求使用
初始的时候 创建了一些连接 最小连接数 请求并发的时候 创建更多的连接 最大连接
常见的连接池
dbcp(spring集成)
c3p0
druid(常用)
使用dbcp连接池
注意:在使用连接池之前需要导入相应的包
Properties prop =new Properties();
prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"))
DataSource ds = BasicDataSourceFactory.createDataSource(prop);
Connection conn=ds.getConnection();