java.sql包只是提供一套连接数据库的标准接口,这些接口的具体实现就是各个厂家提供的自己的接口实现类,这些类的集合就是JDBC
概述
PreparedStatement
由于Statement接口有SQL注入等严重弊端(无法处理blob类型),所以使用Statement的子接口PreparedStatement:(预编译SQL语句)
连接数据库工具类
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
/**
* @Author: xiang
* @Date: 2021/3/21 12:06
*/
public class DBUtils {
/**
* Connection of Database
* @return Connection
* @throws Exception
*/
public static Connection getConn() throws Exception {
//1. 资源绑定器读取信息
ResourceBundle bundle = ResourceBundle.getBundle("info");
String user = bundle.getString("user");
String password = bundle.getString("password");
String url = bundle.getString("url");
String driverClass = bundle.getString("driverClass");
//2. 加载并注册驱动程序
Class clazz = Class.forName(driverClass);
//3. 创建连接对象
return DriverManager.getConnection(url, user, password);
}
/**
* 通用的增删改 insert delete update
* @param sql
* @param args
* @return 改变行数
*/
public static int idu(String sql, Object ...args){
Connection conn = null;
PreparedStatement ps = null;
try {
conn = DBUtils.getConn();
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.closeResource(conn,ps);
}
return 0;
}
/**
* 因为ps.execute()是有返回集(查询)时才是true,没有返回集(增删改)时返回false
* 所以我们选用executeUpdate()
*/
/**
* ORM编程思想(object relational mapping):对象关系映射
* 一个数据表对应一个Java类
* 表中一行记录代表一个Java对象
* 表中一个字段代表一个Java对象属性
*/
/**
* 针对不同表通用查询
* @param clazz 返回集合中对象类
* @param sql
* @param args
* @param <T>
* @return
*/
public <T> List<T> getForList(Class<T> clazz, String sql, Object... args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtils.getConn();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据 :ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<>();
/**
* 执行查询初始都指向第一个前面
* 而ResultSet对象的next()不仅可以检测是否有下一个,而且还能让指针下移返回对应值
*
* 对比构造器iterator的只能hasNext()判断有无下一个,而next()只能让指针下移返回对应值
* 所以iterator遍历需要两者配合
*/
while (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列:给t对象指定的属性赋值
for (int i = 0; i < columnCount; i++) {
// 获取列值
Object columValue = rs.getObject(i + 1);
// 获取每个列的列名
// String columnName = rsmd.getColumnName(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
// 给t对象指定的columnName属性,赋值为columValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.closeResource(conn, ps, rs);
}
return null;
}
/**
* shutdown the Connection of Database
* @param conn
* @param ps
*/
public static void closeResource(Connection conn, PreparedStatement ps){
if (conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (ps != null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
// 重载
public static void closeResource(Connection conn, PreparedStatement ps, ResultSet rs){
if (conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (ps != null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
拓展
获得结果集ResultSet的列数,列名:
数据类型对应
Blob类型的变量
存取大数据量的容器
注意:(MySQL默认插入内容最大为1M,所以需要修改配置文件)
存储过程
传入inputStream就可以了
ps.setBlob(int parameterIndex, InputStream inputStream);
java.util.Date date = format.parse("2021-09-12");
FileInputStream phoneStream = new FileInputStream("adcb.jpeg");//idea默认路径下找
DBUtils.idu(sql,...,...,new Date(date.getTime()),phoneStream);
下载过程
PreparedStatement批量插入优势
批量插入优化
使用batch
再利用事务
事务
事务特点
隔离级别主要在read commited 和 repeatable read 之间选择
JDBC实现事务
JDBC设置隔离级别
当然数据库服务重启后失效