目录
- 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的写法
@Override
public 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 {
@Test
public 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写法
@Override
public 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
@Test
public 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发送到数据库了,只是这时还不会执行sql
select * 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
@Override
public 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
// 登录的第二种实现方式
@Test
public 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("登录成功");
}
}
}
事务
@Test
public 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 block
e.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
@Override
public 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
@Test
public 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{
@Override
public void insert(Student stu) {
String sql="insert into student(name,password) values(?,?)";
excuteUpdate(sql, stu.getName(),stu.getPassword());
}
@Override
public void update(Student stu) {
String sql = "update student set name=?,password=? where id =?";
excuteUpdate(sql, stu.getName(),stu.getPassword(),stu.getId());
}
@Override
public void delete(Student stu) {
String sql = "delete from student where id = ?";
excuteUpdate(sql, stu.getId());
}
}
JDBCTest
@Test
public 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);
}