目录
- PreparedStatement 查询
- 1.sql注入
- 2.Statement分析 (面试题)
- 3.PreparedStatement (面试题)
- 登录功能的完善
- 事务
- 链接池
- 概念
- 实现
- DBCP连接池实现
- 第一种配置方式
- 第二种配置方式
- 返回主键
- BaseDao的抽取
PreparedStatement 查询
1.sql注入
就是在sql的字符串拼接的时候,加入了特定的条件判断,
如:SELECT * FROM student where name=’ 小坤坤255255 ’ OR 1=1
代码
public class StudentDaoImpl implements IStudentDao{//Statement的写法@Overridepublic Student login(String name, String Password) {//通过工具类获取连接Connection conn = JDBCUtil.Instance().getconn();Statement State =null;ResultSet rs=null;Student student = new Student();try {State = conn.createStatement();rs = State.executeQuery("select * from student where name='"+name+"'and password ='"+Password+"'");while (rs.next()) {student.setId(rs.getInt("id"));student.setName(rs.getString("name"));student.setPassword(rs.getString("password"));} } catch (SQLException e) {e.printStackTrace();}finally {JDBCUtil.Instance().close(rs, State, conn);}return student;}
}
public class JDBCTest {@Testpublic void testName() throws Exception {StudentDaoImpl studentDaoImpl = new StudentDaoImpl();//正常的代码
// Student stu = studentDaoImpl.login("网通", "123");//sql注入的代码Student stu = studentDaoImpl.login("网通", "123' or '1=1");System.out.println(stu);if(stu.getName()!=null){System.out.println("账号存在登录成功");}else{System.out.println("账号不存在 ..登录失败");}}
}
2.Statement分析 (面试题)
1.通过上面sql注入的案例我们发现 Statement 它可能会导致sql注入的问题
2.通过这几天的sql的书写我发现 Statement 拼接sql相当复杂,稍微有一点点差错就会导致sql语句有问题
解决方法:PreparedStatement
3.PreparedStatement (面试题)
PreparedStatement 很好的解决了Statement的问题
1.不用担心注入问题(双引号之内看成一个整体的字符串而不是两个字符串和一个关键字),
2.sql语句不用复杂拼接,
3.会预处理sql语句,执行速度也更快
代码:
StudentDaoImpl
//PreparedStatement写法@Overridepublic Student login(String name, String Password) {Connection conn = JDBCUtil2.Instance().getconn();PreparedStatement ps=null;ResultSet rs =null;Student student = new Student();try {ps= conn.prepareStatement("select * from student where name=? and password=?");ps.setString(1, name);ps.setString(2, Password);rs = ps.executeQuery();while(rs.next()){student.setId(rs.getInt("id"));student.setName(rs.getString("name"));student.setPassword(rs.getString("password"));}} catch (SQLException e) {e.printStackTrace();}finally {JDBCUtil2.Instance().close(rs, ps, conn);}return student;}
JDBCTest
@Testpublic void testName() throws Exception {StudentDaoImpl studentDaoImpl = new StudentDaoImpl();//正常的代码
// Student stu = studentDaoImpl.login("网通", "123");//sql注入的代码Student stu = studentDaoImpl.login("网通", "123' or '1=1");System.out.println(stu);if(stu.getName()!=null){System.out.println("账号存在登录成功");}else{System.out.println("账号不存在 ..登录失败");}}
问题:PreparedStatement和Statement 不是同一个类为什么关资源的时候可以传PreparedStatement
因为 PreparedStatement 继承了 Statement,(多态)
PreparedStatement :
// 预处理 这时候就会把sql发送到数据库了,只是这时还不会执行sqlselect * from student where name=? and password=? //变量位置使用?先占住,(这时已经发了sql语句了)ps= conn.prepareStatement("select * from student where name=? and password=?");// 把?替换成对应的值ps.setString(1, name);ps.setString(2, Password);// 执行sql 这时的执行就是一个执行命令,不会发sql语句(前面已发)rs = ps.executeQuery();
Statement:
//创建 Statement 对象 State = conn.createStatement();// 发送并执行sql rs = State.executeQuery("select * from student where name='"+name+"'and password ='"+Password+"'");
PreparedStatement 是 Statement 子类,速度比Statement 快,能避免sql 注入,可以不用拼接sql语句
登录功能的完善
StudentDaoImpl
@Overridepublic Student QueryByUsername(String name) {Connection conn = JDBCUtil2.Instance().getconn();PreparedStatement ps = null;ResultSet rs = null;Student student = new Student();try {ps = conn.prepareStatement("select * from student where name=?");ps.setString(1, name);rs = ps.executeQuery();while (rs.next()) {student.setId(rs.getInt("id"));student.setName(rs.getString("name"));student.setPassword(rs.getString("password"));}} catch (SQLException e) {e.printStackTrace();} finally {JDBCUtil2.Instance().close(rs, ps, conn);}return student;}
JDBCTest
// 登录的第二种实现方式@Testpublic void login() throws Exception {StudentDaoImpl studentDaoImpl = new StudentDaoImpl();// 查询是小坤坤(用户名)的信息,这个用户名 应该是前台(浏览器) 用户 传过来的 -- 模拟Student student = studentDaoImpl.QueryByUsername("小坤坤");// 判断用户名是否存在if (student.getName() == null) {System.out.println("账号不存在");}// else 就是账号存在else {// 判断这个账号的密码是否正确 (这个密码应该是前台(浏览器) 用户 传过来的)if (!"8848".equals(student.getPassword())) {System.err.println("密码错误");} else {System.out.println("登录成功");}}}
事务
@Testpublic void Testtrans() throws Exception {Connection connection = null;PreparedStatement ps = null;PreparedStatement ps2 = null;try {connection = JDBCUtil2.Instance().getconn();// 不提交事务 (sql执行了,改变了数据库的数据,但是后面没有写提交事务数据库就不能有变化),connection.setAutoCommit(false);String sql = "update bank set money=money-1000 where name='过儿'";ps = connection.prepareStatement(sql);ps.execute();// 在这个位置 出现异常int a=0/0;String sql2 = "update bank set money=money+1000 where name='姑姑'";ps2 = connection.prepareStatement(sql2);ps2.execute();// 提交事物 (数据库可以发生变化了)connection.commit();} catch (Exception e) {// 回滚 (你数据库改变了之后我还是可以回滚)/*当我们把自动提交关闭,那sql就不是提交执行,于是我们一定要记住,当我们一个整体功能完成之后,自己要手动进行提交;--conn.commit但是失败之后,要记住数据回滚*/connection.rollback();} finally {ps2.close();ps.close();connection.close();}}
ACID (面试)
事务 : 一组操作 要么都成功 要么都失败
事务具有4个特征,分别是原子性、一致性、隔离性和持久性,简称事务的ACID特性;
原子性(atomicity) :一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作
一致性(consistency) : 一个事务执行前后,数据库都处于一致性状态
隔离性(isolation): 每一个事务都是单独的,事务和事务之间不影响
持久性(durability): 事务执行完了, 持久化到数据库
链接池
概念
你创建了一个池塘 池塘里面你放了很多链接 用完了就放回去 -->节省开关链接的时间
实现
在Java中,连接池使用javax.sql.DataSource接口来表示连接池,这里的DataSource就是连接池,连接池就是DataSource。
DataSource是接口,和JDBC一样,是Sun公司开发的一套接口,需要各大厂商实现;
需要导入相应包—导包…
所以使用连接池,首先需要导包;
常用的DataSource的实现有下面两种方式:
DBCP: Spring推荐的(Spring框架已经集成DBCP)
C3P0: Hibernate推荐的(早期)(Hibernate框架已经集成C3P0)持久层
DBCP连接池实现
1.导入jar包
commons-dbcp-1.3.jar,commons-pool-1.5.6.jar
2.代码
BasicDataSource就是DBCP的连接池实现
第一种配置方式
public class JDBCUtil {// 构造方法私有化private JDBCUtil() {};private static JDBCUtil instance;// 一启动就加载驱动 只执行一次static Properties ps = null;static BasicDataSource ds = null;static {ps = new Properties();try {ps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));ds = new BasicDataSource();ds.setDriverClassName(ps.getProperty("dirverClassName"));ds.setUsername(ps.getProperty("username"));ds.setPassword(ps.getProperty("password"));ds.setUrl(ps.getProperty("url"));} catch (IOException e) {e.printStackTrace();}/* try {ps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));Class.forName(ps.getProperty("dirverClassName"));} catch (Exception e) {e.printStackTrace();}*/instance = new JDBCUtil();}public static JDBCUtil Instance() {return instance;}// 写加载数据库的驱动public Connection getconn() {Connection connection = null;try {//换成新的获取连接池的方式connection = ds.getConnection();// connection = DriverManager.getConnection(ps.getProperty("url"),// ps.getProperty("username"), ps.getProperty("password"));} catch (Exception e) {e.printStackTrace();}return connection;}// 关闭资源public void close(ResultSet rs, Statement State, Connection conn) {try {if (rs != null) {rs.close();}} catch (SQLException e) {e.printStackTrace();} finally {try {if (State != null) {State.close();}} catch (SQLException e) {e.printStackTrace();} finally {try {if (conn != null) {conn.close();}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
}
public class test {public static void main(String[] args) {Connection connection = JDBCUtil.Instance().getconn();try {String sql = "update bank set money=money-500 where name='过儿'";PreparedStatement ps = connection.prepareStatement(sql);ps.execute();} catch (SQLException e) {e.printStackTrace();}}
}
第二种配置方式
public class JDBCUtil2 {// 构造方法私有化private JDBCUtil2() {};private static JDBCUtil2 instance;// 一启动就加载驱动 只执行一次static Properties ps = null;static DataSource ds = null;static {ps = new Properties();try {ps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));// 创建连接池ds = BasicDataSourceFactory.createDataSource(ps);} catch (Exception e) {e.printStackTrace();}// try {// ps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));// Class.forName(ps.getProperty("dirverClassName"));// } catch (Exception e) {// e.printStackTrace();// }instance = new JDBCUtil2();}public static JDBCUtil2 Instance() {return instance;}// 写加载数据库的驱动public Connection getconn() {Connection connection = null;try {//换成新的获取连接池的方式connection = ds.getConnection();// connection = DriverManager.getConnection(ps.getProperty("url"),// ps.getProperty("username"), ps.getProperty("password"));} catch (Exception e) {e.printStackTrace();}return connection;}// 关闭资源public void close(ResultSet rs, Statement State, Connection conn) {try {if (rs != null) {rs.close();}} catch (SQLException e) {e.printStackTrace();} finally {try {if (State != null) {State.close();}} catch (SQLException e) {e.printStackTrace();} finally {try {if (conn != null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}}
}
public class test {public static void main(String[] args) {Connection connection = JDBCUtil2.Instance().getconn();try {String sql = "update bank set money=money-500 where name='过儿'";PreparedStatement ps = connection.prepareStatement(sql);ps.execute();} catch (SQLException e) {e.printStackTrace();}}
}
返回主键
场景举例:先后添加product和product_stock时,需要拿到product插入时自增的id存到product_stock的product_id里
看文档做
StudentDaoImpl
@Overridepublic void insert(Student stu) {Connection conn = JDBCUtil.Instance().getconn();PreparedStatement ps = null;try {String sql = "insert into student(name,password) values(?,?)";ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);ps.setString(1, stu.getName());ps.setString(2, stu.getPassword());ps.executeUpdate();ResultSet rs = ps.getGeneratedKeys();while (rs.next()) {System.err.println(rs.getInt(1));}} catch (SQLException e) {e.printStackTrace();}}
JDBCTest
@Testpublic void addStudent() throws Exception {StudentDaoImpl studentDaoImpl = new StudentDaoImpl();Student stu = new Student();stu.setName("小波波");stu.setPassword("857857958958");studentDaoImpl.insert(stu);}
BaseDao的抽取
BaseDao
public class BaseDao {public void excuteUpdate(String sql, Object... objects) {Connection conn = JDBCUtil2.Instance().getconn();PreparedStatement ps = null;try {ps = conn.prepareStatement(sql);for (int i = 0; i < objects.length; i++) {ps.setObject(i + 1, objects[i]);}ps.execute();} catch (SQLException e) {e.printStackTrace();} finally {JDBCUtil2.Instance().close(null, ps, conn);}}
}
实现类:
public class StudentDaoImpl extends BaseDao implements IStudentDao{@Overridepublic void insert(Student stu) {String sql="insert into student(name,password) values(?,?)";excuteUpdate(sql, stu.getName(),stu.getPassword());}@Overridepublic void update(Student stu) {String sql = "update student set name=?,password=? where id =?";excuteUpdate(sql, stu.getName(),stu.getPassword(),stu.getId());}@Overridepublic void delete(Student stu) {String sql = "delete from student where id = ?";excuteUpdate(sql, stu.getId());}
}
JDBCTest
@Testpublic void addStudent() throws Exception {StudentDaoImpl studentDaoImpl = new StudentDaoImpl();Student stu = new Student();stu.setName("小波波");stu.setPassword("857857");stu.setId(254172);
// studentDaoImpl.insert(stu);
// studentDaoImpl.delete(stu);studentDaoImpl.update(stu);}