PreparedStatement:
之后编写代码都会使用PreparedStatement代替Statement,原因是因为Statement中需要自己拼接sql语句而PreparedStatement不需要自己拼接。
代码封装:
在之前所写的代码中会发现存在许多重复的代码,这样就会影响程序的效应,为了优化,我们需要将公共的部分封装起来,达到使用时直接调用即可。
一、PreparedStatement接口
Statement:
作用:发送SQL语句,通知数据库执行并得到结果
缺点:
1、SQL语句拼接容易出错
2、可读性差
3、SQL攻击或者SQL注入 系统安全问题
PreparedStatement:
优点:
1、带预编译的功能,让数据库开启预编译
2、sql语句中的值使用?占位可读性更高
3、使用?占位不需要拼接SQL,SQL攻击/SQL注入失败
提供的方法:
1、setXxx(int index,值) 给指定位置的?赋值,XXX可以是string/int/Object
2、执行增删改查的方法
int excuteUpdate() 增删改,不需要传sql
ResultSet executeQuery() 查询,不需要传sql
代码步骤:
1、创建PreparedStatement对象 pre
2、将SQL语句需要拼接的内容改为?
3、通过connection创建PreparedStatement,里面传入SQL变量进行预编译
4、使用PreparedStatement提供的setXxx(int index,值)给?赋值,index从1开始
(一般使用setXxx()方法,都会自动添加''不需要手动添加引号)
5、提供了增删改查的方法
int executeUpdate()
ResultSet executeQuery()
对于Statement而言,存在安全问题,所以java提供了另一个接口PreparedStatement,该接口是Statement的子类,功能更加的强大,所以以后都会使用PreparedStatement替代Statement
二、代码封装
1、创建一个后缀名为properties的全局配置文件,将数据库的地址、用户名、密码等公共部分写入,该文件放在src目录下
# 驱动类
driverClass=com.mysql.jdbc.Driver
# 数据库url
url=jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=utf8&useSSL=false
# 用户名
username=root
# 密码
password=123456
2、创建一个pojo的包,该包专门放置对应表的类,例如操作的是tb_user表即可在该pojo包下创建对应的User类,将表中的字段名在类中使用private创建,并提供get和set方法以及toString方法,如果手动定义了构造函数还需要再自行添加一个无参的构造函数,(这里使用tb_user表===对应类名为User)
package pojo;
public class User {
private String sname;
private Integer spwd;
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Integer getSpwd() {
return spwd;
}
public void setSpwd(Integer spwd) {
this.spwd = spwd;
}
@Override
public String toString() {
return "User{" +
"sname='" + sname + '\'' +
", spwd=" + spwd +
'}';
}
}
3、创建一个util包,该包主要存放工具类,如JDBCUtil,该类主要用于编写对数据库公共的增删改查的方法,需要创建静态的Properties对象读取全局配置文件,将建立连接、释放资源、创建PreparedStatement、增删改、查的方法放入该类中,注意:查询方法中返回的是list集合,需要将ResultSet拿到的结果集通过反射的作用将User类中私有的属性添加到list集合中,由于不确定存放的类型,所以需要使用到泛型
package util;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class JDBCUtil {
// 创建一个properties集合对象
static Properties pops = new Properties();
// 静态代码块,程序在运行之前就会执行
static {
// 需要使用相对路径 类加载器(输入流)只加载src目录下的资源将PropertiesDemo.java加载到内存中
InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
try {
pops.load(in);
// 1、加载驱动类
Class.forName(pops.getProperty("driverClass"));
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
// todo 建立连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(pops.getProperty("url"), pops.getProperty("username"), pops.getProperty("password"));
}
// todo 释放资源
public static void close(PreparedStatement psmt, ResultSet rs, Connection conn) {
try {
//6、关闭资源
if (psmt != null) {
psmt.close();
}
if (rs != null) {
rs.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// todo 公共的创建PreparedStatement方法
public static PreparedStatement createPreparedStatement(Connection conn, String sql, Object... params) throws SQLException {
conn = JDBCUtil.getConnection();
PreparedStatement psmt = conn.prepareStatement(sql);
// 给?赋值
if (params != null && params.length > 0) {
for (int i = 0; i < params.length; i++) {
psmt.setObject(i + 1, params[i]);
}
}
return psmt;
}
// todo 增删改的方法
public static int executeUpdate(String sql, Object...items) {
Connection conn = null;
PreparedStatement psmt = null;
try {
conn = JDBCUtil.getConnection();
psmt = createPreparedStatement(conn, sql, items);
return psmt.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return 0;
}
// todo 查的方法
public static <T> List<T> executeQuery(String sql, Class<T> clazz, Object... items) {
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
// 获取连接
conn = JDBCUtil.getConnection();
// 创建prepareStatement
psmt = createPreparedStatement(conn, sql, items);
// 获取结果集
rs = psmt.executeQuery();
// 将结果集中的数据一条一条的放入list集合中,需要使用到反射,使用表对应创建的类的字节码文件并得到实例化对象最后获得字段(无论私有还是非私有)
// 创建一个T类型的集合
List<T> list = new ArrayList<>();
// 遍历循环
while (rs.next()) {
// 1、获得实例化对象
T t = clazz.newInstance();
// 2、获取类中每个字段
Field[] fields = clazz.getDeclaredFields();
// 3、遍历字段
for (Field field : fields) {
// 4、暴力反射
field.setAccessible(true);
/* 5、给属性赋值 底层调用 对象.属性 = 值
getObject(列索引)
getObject(列名) 约定: 属性名与列名一样,赋值
如果属性名与列名不一样: 列名:user_name username 属性名:username
给列名取一个属性名的别名*/
field.set(t, rs.getObject(field.getName()));
}
// 6、将fileds数组给list集合
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(psmt,rs,conn);
}
return null;
}
}
4、创建一个dao包,该包主要存放对表进行操作的接口,将方法都写在该接口下即可
package Dao;
import pojo.Student;
import pojo.User;
import java.util.List;
/*todo User数据访问层
* 对User的增删改查
* 位于dao包下的都是接口
* Impl包下的是dao包接口的实现类
* */
public interface StudentDao {
/*
* 根据id查询用户
* */
public Student queryBySname(Integer id);
/*
* 查询所有
* */
public List<Student> queryAll();
/*
* 插入User数据
* */
public int insert(Student s);
/*
* 根据sname删除
* */
public int deleteById(Integer id);
/*
* 修改用户,修改数据,一定不能修改主键
* */
public int update(Student s);
}
5、在dao包下创建一个Impl包,专门存放对应接口的实现类,下次增删改查就可以直接调用该实现类中的方法了。
package Dao.Impl;
import Dao.StudentDao;
import pojo.Student;
import util.JDBCUtil;
import java.util.List;
/*
* todo 该类为StudentDao的实现类
*
* */
public class StudentDaoImpl implements StudentDao {
@Override
public Student queryBySname(Integer id) {
String sql = "select * from tb_student WHERE id = ?";
List<Student> list = JDBCUtil.executeQuery(sql, Student.class, id);
return list == null || list.isEmpty() ? null : list.get(0);
}
@Override
public List<Student> queryAll() {
String sql = "select * from tb_student";
List<Student> students = JDBCUtil.executeQuery(sql, Student.class);
return students;
}
@Override
public int insert(Student s) {
String sql = "inset into tb_student values(?,?,?,?)";
return JDBCUtil.executeUpdate(sql, s.getId(), s.getName(), s.getSex(), s.getAge());
}
@Override
public int deleteById(Integer id) {
String sql = "delete from tb_student where id=?";
return JDBCUtil.executeUpdate(sql,id);
}
@Override
public int update(Student s) {
return 0;
}
}