1、PreparedStatement 与 Statement 的异同?
PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句
PreparedStatement 能最大可能提高性能:
PreparedStatement 可以防止 SQL 注入 (通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。)
2、Druid(德鲁伊)数据库连接池
Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。
package com.atguigu.druid;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class TestDruid {
public static void main(String[] args) throws Exception {
Properties pro = new Properties(); pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
其中,src下的配置文件为:【druid.properties】
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.cj.jdbc.Driver
initialSize=10
maxActive=20
maxWait=1000
filters=wall
3、数据库连接方式(最终版)
public void testConnection5() throws Exception {
//1.加载配置文件
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
//2.读取配置信息
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4.获取连接
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);
}
配置文件【jdbc.properties】
user=root
password=123456
url=jdbc:mysql://localhost:3306/test
driverClass=com.mysql.cj.jdbc.Driver
使用配置文件的好处:
①实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码
②如果修改了配置信息,省去重新编译的过程。
4、考虑上事务的通用增删改操作
public void update(Connection conn, String sql, Object... args) { //sql中占位符的个数与可变形参的长度相同!
PreparedStatement ps = null;
try {
//1.预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //小心参数声明错误!
}
//3.执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.资源关闭
JDBCUtil.closeResource(null, ps);
}
}
测试事务的增删改操作
public void testJDBCTransaction() {
Connection conn = null;
try {
// 1.获取数据库连接
conn = JDBCUtil.getConnection();
// 2.开启事务,取消事务数据自动提交
conn.setAutoCommit(false);
// 3.进行数据库操作
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn, sql1, "AA");
// 模拟网络异常
//System.out.println(10 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn, sql2, "BB");
// 4.若没有异常,则提交事务
conn.commit();
} catch (Exception e) {
e.printStackTrace();
// 5.若有异常,则回滚事务
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
//6.恢复每次DML操作的自动提交功能,主要针对数据库连接池的使用
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
//7.关闭连接
JDBCUtil.closeResource(conn, null, null);
}
}
5、考虑上事务的针对于不同表的通用的查询操作,返回一条记录
public <T> T getInstance(Connection conn, Class<T> clazz, String sql, Object... args) { //sql中占位符的个数与可变形参的长度相同!
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //小心参数声明错误!
}
//3.执行,获取结果集
rs = ps.executeQuery();
//获取结果集的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//多个数据用while(rs.next())
if (rs.next()) {
T t = clazz.getDeclaredConstructor().newInstance();
//处理结果集一行数据中的每一列
for (int i = 0; i < columnCount; i++) {
//获取每一个列的列值:通过ResultSet
Object columnValue = rs.getObject(i + 1);
/*//获取每一个列的列名:通过ResultSetMetaData.getColumnName,不推荐使用
String columnName = rsmd.getColumnName(i + 1);*/
//获取每一个列的别名:通过ResultSetMetaData.getColumnLabel
String columnLabel = rsmd.getColumnLabel(i + 1);
//给cust对象指定的columnName属性赋值为columnValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
//setAccessible()值为true表示反射的对象在使用时应该取消Java语言访问检查。由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
field.setAccessible(true);
field.set(t, columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.资源关闭
JDBCUtil.closeResource(null, ps, rs);
}
return null;
}
6、考虑上事务针对于不同表的通用的查询操作,返回多条数据集合
public <T> List<T> getForList(Connection conn, Class<T> clazz, String sql, Object... args) { //sql中占位符的个数与可变形参的长度相同!
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //小心参数声明错误!
}
//3.执行,获取结果集
rs = ps.executeQuery();
//获取结果集的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<>();
//多个数据用while(rs.next())
while (rs.next()) {
T t = clazz.getDeclaredConstructor().newInstance();
//处理结果集一行数据中的每一列:给t对象指定的属性赋值
for (int i = 0; i < columnCount; i++) {
//获取每一个列的列值:通过ResultSet
Object columnValue = rs.getObject(i + 1);
/*//获取每一个列的列名:通过ResultSetMetaData.getColumnName,不推荐使用
String columnName = rsmd.getColumnName(i + 1);*/
//获取每一个列的别名:通过ResultSetMetaData.getColumnLabel
String columnLabel = rsmd.getColumnLabel(i + 1);
//给cust对象指定的columnName属性赋值为columnValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
//setAccessible()值为true表示反射的对象在使用时应该取消Java语言访问检查。由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
field.setAccessible(true);
field.set(t, columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.资源关闭
JDBCUtil.closeResource(null, ps, rs);
}
return null;
}
7、使用PreparedStatement实现批量数据的操作(批量插入)
//update、delete本身就具有批量操作的的效果
//1、使用 addBatch() / executeBatch() / clearBatch()
//2、mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。?rewriteBatchedStatements=true写在配置文件的url后面
//3、使用更新的mysql 驱动:mysql-connector-java-5.1.37-bin.jar
public static void testInsert(){
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtil.getConnection();
//设置不允许自动提交数据
conn.setAutoCommit(false);
String sql = "insert into goods(name) values(?);";
ps = conn.prepareStatement(sql);
for(int i = 1;i <= 20000;i++){
ps.setString(1, "name_" + i);
//1.“攒”sql
ps.addBatch();
if (i % 500 == 0){
//2.执行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
//提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.closeResource(conn,ps);
}
}
8、BaseDAO.java 封装了针对数据表的通用操作,写接口以及继承BaseDAO和实现接口的实现类
public abstract class BaseDAO<T> {
private Class<T> clazz = null;
// public BaseDAO(){}
{
//获取当前BaseDAO的子类继承的父类中泛型
Type genericSuperclass = this.getClass().getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
Type[] typeArguments = paramType.getActualTypeArguments(); //获取了父类的泛型参数
clazz = (Class<T>) typeArguments[0]; //泛型的第一个参数
}
//考虑上事务的通用增删改操作
public void update(Connection conn, String sql, Object... args) { //sql中占位符的个数与可变形参的长度相同!
PreparedStatement ps = null;
try {
//1.预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //小心参数声明错误!
}
//3.执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.资源关闭
JDBCUtil.closeResource(null, ps);
}
}
//考虑上事务的针对于不同表的通用的查询操作,返回一条记录
public T getInstance(Connection conn, String sql, Object... args) { //sql中占位符的个数与可变形参的长度相同!
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //小心参数声明错误!
}
//3.执行,获取结果集
rs = ps.executeQuery();
//获取结果集的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//多个数据用while(rs.next())
if (rs.next()) {
T t = clazz.getDeclaredConstructor().newInstance();
//处理结果集一行数据中的每一列
for (int i = 0; i < columnCount; i++) {
//获取每一个列的列值:通过ResultSet
Object columnValue = rs.getObject(i + 1);
/*//获取每一个列的列名:通过ResultSetMetaData.getColumnName,不推荐使用
String columnName = rsmd.getColumnName(i + 1);*/
//获取每一个列的别名:通过ResultSetMetaData.getColumnLabel
String columnLabel = rsmd.getColumnLabel(i + 1);
//给cust对象指定的columnName属性赋值为columnValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
//setAccessible()值为true表示反射的对象在使用时应该取消Java语言访问检查。由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
field.setAccessible(true);
field.set(t, columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.资源关闭
JDBCUtil.closeResource(null, ps, rs);
}
return null;
}
//考虑上事务的针对于不同表的通用的查询操作,返回多条数据构成的集合
//list.forEach(System.out::println);
public List<T> getForList(Connection conn, String sql, Object... args) { //sql中占位符的个数与可变形参的长度相同!
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //小心参数声明错误!
}
//3.执行,获取结果集
rs = ps.executeQuery();
//获取结果集的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<>();
//多个数据用while(rs.next())
while (rs.next()) {
T t = clazz.getDeclaredConstructor().newInstance();
//处理结果集一行数据中的每一列:给t对象指定的属性赋值
for (int i = 0; i < columnCount; i++) {
//获取每一个列的列值:通过ResultSet
Object columnValue = rs.getObject(i + 1);
/*//获取每一个列的列名:通过ResultSetMetaData.getColumnName,不推荐使用
String columnName = rsmd.getColumnName(i + 1);*/
//获取每一个列的别名:通过ResultSetMetaData.getColumnLabel
String columnLabel = rsmd.getColumnLabel(i + 1);
//给cust对象指定的columnName属性赋值为columnValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
//setAccessible()值为true表示反射的对象在使用时应该取消Java语言访问检查。由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
field.setAccessible(true);
field.set(t, columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.资源关闭
JDBCUtil.closeResource(null, ps, rs);
}
return null;
}
//用于查询特殊值的通用方法
public <E> E getValue(Connection conn, String sql, Object... args) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //小心参数声明错误!
}
//3.执行,获取结果集
rs = ps.executeQuery();
if (rs.next()) {
return (E) rs.getObject(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(null, ps, rs);
}
return null;
}
}
接口 【CustomerDAO】
//此接口用于规范对于customers表的常用操作
public interface CustomerDAO {
//将cust对象添加到数据库中
void insert(Connection conn, Customer cust);
//根据指定的id,删除数据表中的一条记录
void deleteById(Connection conn, int id);
//根据内存中的cust对象,修改数据表中指定记录
void update(Connection conn, Customer cust);
//根据指定的id,查询得到对应的Customer对象
Customer getCustomerById(Connection conn, int id);
//查询表中所有记录构成的集合
List<Customer> getAll(Connection conn);
//返回数据表中的数据的条目数
Long getCount(Connection conn);
//返回数据表中的最大的生日
Date getMaxBirth(Connection conn);
}
实现类 【CustomerDAOImpl】
public class CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO {
@Override
public void insert(Connection conn, Customer cust) {
String sql = "insert into customers(name,email,birth) values(?,?,?);";
update(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth());
}
@Override
public void deleteById(Connection conn, int id) {
String sql = "delete from customers where id = ?;";
update(conn, sql, id);
}
@Override
public void update(Connection conn, Customer cust) {
String sql = "update customers set id = ?, email = ?, birth = ? where id = ?;";
update(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth(), cust.getId());
}
@Override
public Customer getCustomerById(Connection conn, int id) {
String sql = "select id,email,birth from customer where id = ?;";
Customer customer = getInstance(conn, sql, id);
return customer;
}
@Override
public List<Customer> getAll(Connection conn) {
String sql = "select id,email,birth from customer;";
List<Customer> list = getForList(conn, sql);
return list;
}
@Override
public Long getCount(Connection conn) {
String sql = "select count(*) from customer;";
return getValue(conn, sql);
}
@Override
public Date getMaxBirth(Connection conn) {
String sql = "select max(birth) from customer;";
return getValue(conn, sql);
}
}