感谢尚硅谷,感谢宋红康老师
视频地址:
尚硅谷JDBC核心技术视频教程(康师傅带你一站式搞定jdbc)_哔哩哔哩_bilibili
目录
4、PreparedStatement替换Statement实现CRUD操作
5、PreparedStatement操作Blob类型的变量
7、PreparedStatement与Statement的异同
一、JDBC介绍
1、数据的持久化
持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。
2.、JDBC的理解
JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)
简单理解为:JDBC,是SUN提供的一套 API,使用这套API可以实现对具体数据库的操作(获取连接、关闭连接、DML、DDL、DCL)
3、图示理解
好处
- 从开发程序员的角度:不需要关注具体的数据库的细节
- 数据库厂商:只需要提供标准的具体实现。
4、数据库的驱动
数据库厂商针对于JDBC这套接口,提供的具体实现类的集合。
类似:
5、面向接口编程的思想
JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。
二、JDBC操作数据库
1、数据库的连接
public class ConnectionTest {
@Test
public void getConnection5() throws Exception{
//1.读取配置文件中的4个基本信息
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
}
jdbc.properties
user=root
password=12345
url=jdbc:mysql://localhost:3306/test
driverClass=com.mysql.jdbc.Driver
此种方式的好处
- 实现了数据与代码的分离。实现了解耦。
- 如果需要修改配置文件信息,可以避免程序重新打包。
2、JDBCUtils
public class JDBCUtils {
/**
* 连接资源
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception{
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties.txt");
Properties pros = new Properties();
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 关闭资源
* @param conn
* @param ps
*/
//使用dbutils.jar中提供的Dbutils工具类实现资源的关闭
public static void closeRessourse(Connection conn, Statement ps){
try {
if(ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭资源
* @param conn
* @param ps
* @param rs
*/
public static void closeRessourse(Connection conn, Statement ps, ResultSet rs){
try {
if(ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(rs!=null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3、Statement接口实现CRUD操作(了解)
ResultSet
- Object getObject(String columnLabel):根据列名获取对应数据表中的数据
- ResultSetMetaData getMetaData():返回一个 ResultSetMetaData 对象,它包含查询结果集ResultSet对象的元数据(描述数据的数据,又称为中继数据),可以得到结果集中有哪些列,列名,列的别名等等。
ResultSetMetaData:
- int getColumnCount():获取结果集的列数
- String getColumnName(int column):获取列的名称
- String getColumnLabel(int column):获取列的别名
public class User {
private String user;
private String password;
}
/*
* 实现对数据表中数据的增删改操作
*/
public void update(String sql){
Connection conn = null;
Statement st = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.创建一个Statement的实例
st = conn.createStatement();
//3.根据提供的sql语句,执行
st.execute(sql);
} catch (Exception e) {
e.printStackTrace();
}finally{
//4.资源的关闭
JDBCUtils.close(conn, st);
}
}
/*
* 实现对数据表的查询操作。需要使用结果集:ResultSet,
* 返回表中的一条记录
*/
public <T> T get(String sql, Class<T> clazz) {
T t = null;
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
st = conn.createStatement();
rs = st.executeQuery(sql);
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取结果集的列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
// //1. 获取列的名称
// String columnName = rsmd.getColumnName(i+1);
// 1. 获取列的别名
// 注意:获取结果集中(相当于数据表)列的名称(别名)
String columnName = rsmd.getColumnLabel(i + 1);
// 2. 根据列名获取对应数据表中的数据
Object columnVal = rs.getObject(columnName);
// 3. 将数据表中得到的数据,封装进对象
// 注意:反射根据Java中类的属性获取Field对象
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return null;
}
Statement使用的弊端
拼串
String sql = "insert into customers(name,email,birth)values('" + name +"','"+email+"','"+birth+"')";
SQL注入:
SELECT user,password FROM user_table WHERE user = '1' AND password = '1 or 1 = 1'
其他问题:
- Statement没办法操作Blob类型变量
- Statement实现批量插入时,效率较低
4、PreparedStatement替换Statement实现CRUD操作
- PreparedStatement 是Statement的子接口,可以解决Statement的sql注入问题,拼串问题。
- PreparedStatement操作Blob的数据,而Statement做不到。 .
- PreparedStatement可以实现更高效的批量操作。
/**
* 通用的增删该操作
* @param sql
* @param args
* @return
*/
public void update(String sql,Object ...args){//sql中占位符的个数与可变形参的长度相同!
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1, args[i]);//小心参数声明错误!!
}
//4.执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}finally{
//5.资源的关闭
JDBCUtils.closeResource(conn, ps);
}
}
/**
* 针对于不同的表的通用的查询操作,返回表中的一条记录
* @param clazz
* @param sql
* @param args
* @return
*/
public <T> T getInstance(Class<T> clazz,String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
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();
if (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
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);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
/**
* 针对于不同的表的通用的查询操作,返回表中的多条记录
* @param clazz
* @param sql
* @param args
* @return
*/
public <T> List<T> getForList(Class<T> clazz,String sql, Object... args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
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<T>();
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 {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
测试
@Test
public void test1() throws Exception {
String sql="select id,name,email from customers where id < ?";
List<Customer> list = getForList(Customer.class, sql, 12);
list.forEach(System.out::println);
}
@Test
public void test2(){
String sql="delete from customers where id = ? ";
update(sql,3);
}
@Test
public void test3(){
String sql="insert into customers(name,email,birth) values(?,?,?)";
update(sql,"sss","sss","2021-03-10");
}
@Test
public void test3(){
String sql="update `order` set order_name = ? where order_id = ?";
update(sql,"DD",2);
}
5、PreparedStatement操作Blob类型的变量
BLOB (binary large object):二进制大对象,是一个可以存储二进制文件的容器。在计算机中,BLOB常常是数据库中用来存储二进制文件的字段类型。
BLOB是一个大文件,典型的BLOB是一张图片或一个声音文件,由于它们的尺寸,必须使用特殊的方式来处理(例如:上传、下载或者存放到一个数据库)。
向数据表customers中插入Blob类型的字段
@Test
public void testInsert() throws Exception{
Connection conn = JDBCUtils.getConnection();
String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,"袁浩");
ps.setObject(2, "yuan@qq.com");
ps.setObject(3,"1992-09-08");
FileInputStream is = new FileInputStream(new File("girl.jpg"));
ps.setBlob(4, is);
ps.execute();
JDBCUtils.closeResource(conn, ps);
}
查询数据表customers中Blob类型的字段,以文件的方式保存在本地
@Test
public void testQuery(){
Connection conn = null;
PreparedStatement ps = null;
InputStream is = null;
FileOutputStream fos = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select id,name,email,birth,photo from customers where id = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 21);
rs = ps.executeQuery();
if(rs.next()){
// 方式一:
// int id = rs.getInt(1);
// String name = rs.getString(2);
// String email = rs.getString(3);
// Date birth = rs.getDate(4);
//方式二:
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer cust = new Customer(id, name, email, birth);
System.out.println(cust);
//将Blob类型的字段下载下来,以文件的方式保存在本地
Blob photo = rs.getBlob("photo");
is = photo.getBinaryStream();
fos = new FileOutputStream("zhangyuhao.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer, 0, len);
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
JDBCUtils.closeRessourse(conn, ps, rs);
}
}
注意:
6、PreparedStatement实现高效的批量操作
层次一:使用Statement实现
@Test
public void testInsert1(){
Connection conn = null;
Statement st = null;
try {
conn = JDBCUtils.getConnection();
st = conn.createStatement();
long start=System.currentTimeMillis();
for(int i=1;i<20000;i++) {
String sql = "insert into goods(name)values('name_" + i + "')";
st.execute(sql);
}
long end=System.currentTimeMillis();//13579
System.out.println(end-start);
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.closeRessourse(conn,st);
}
}
层次二:使用PreparedStatement替换Statement
@Test
public void testInsert2() throws Exception {
Connection conn = null;
PreparedStatement ps = null;
try {
long start=System.currentTimeMillis();
conn = JDBCUtils.getConnection();
String sql="insert into goods(name) values(?)";
ps = conn.prepareStatement(sql);
for(int i=1;i<20000;i++){
ps.setObject(1,"name_"+i);
ps.execute();
}
long end=System.currentTimeMillis();//14447
System.out.println(end-start);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeRessourse(conn,ps);
}
}
层次三
addBatch()、executeBatch()、clearBatch()
mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。
?rewriteBatchedStatements=true
写在配置文件的url后面
使用更新的mysql 驱动:mysql-connector-java-5.1.37-bin.jar
@Test
public void testInsert3() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start=System.currentTimeMillis();
conn = JDBCUtils.getConnection();
String sql="insert into goods(name) values(?)";
ps = conn.prepareStatement(sql);
for(int i=1;i<20000;i++){
ps.setObject(1,"name_"+i);
//1.攒sql
ps.addBatch();
if(i%500==0){
//2.执行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
long end=System.currentTimeMillis();
System.out.println(end-start);//401
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeRessourse(conn,ps);
}
}
层次四:设置连接不允许自动提交数据
比层次三多了一个这
- conn.setAutoCommit(false);
@Test
public void testInsert4(){
Connection conn = null;
PreparedStatement ps = null;
try {
long start=System.currentTimeMillis();
conn = JDBCUtils.getConnection();
//设置不允许自动提交数据
conn.setAutoCommit(false);
String sql="insert into goods(name) values(?)";
ps = conn.prepareStatement(sql);
for(int i=1;i<20000;i++){
ps.setObject(1,"name_"+i);
//1.赞sql
ps.addBatch();
if(i%500==0){
//2.执行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
//提交数据
conn.commit();
long end=System.currentTimeMillis();
System.out.println(end-start);//338
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeRessourse(conn,ps);
}
}
7、PreparedStatement与Statement的异同
- 接口与子接口的关系
- 开发中,PreparedStatement替换Statement
三、数据库的事务
事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
- 一组逻辑操作单元:一个或多个DML操作
1、事务事务处理的原则
保证所事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。
说明:
1、数据一旦提交,就不可回滚
2、哪些操作会导致数据的自动提交?
1)DML默认情况下,一旦执行,就会自动提交
- set autocommit = false 对DDL操作失效
2)默认在关闭连接时,会自动的提交数据
2、代码实现
@Test
public void testUpdateWithTx() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
System.out.println(conn.getAutoCommit());//true
//1.取消数据的自动提交
conn.setAutoCommit(false);
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");
System.out.println("转账成功");
//2.提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
//3.回滚数据
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
//修改其为自动提交数据
//主要针对于使用数据库连接池的使用
try {
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
JDBCUtils.closeResource(conn, null);
}
}
考虑到事务以后,实现的通用的增删改操作:
public int update(Connection conn,String sql, Object... args) {
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.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 4.资源的关闭
JDBCUtils.closeResource(null, ps);
}
return 0;
}
考虑到事务以后,实现的通用的查询:
public <T> T getInstance(Connection conn,Class<T> clazz,String sql, Object... args) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
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();
if (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
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);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
3、事务的属性
数据操作过程中可能出现的问题:(针对隔离性)
数据库的四种隔离级别:(一致性和并发性:一致性越好,并发性越差)
查看并设置隔离级别:
4、总结
考虑到事务以后的数据库操作(重点)
1、获取数据库的连接
//方式1:手动获取连接 方式2:数据库连接池
Connection conn = JDBCUtils.getConnection();
//体现事务
conn.setAutoCommit(false);
2、如下的多个DML操作,作为一个事务出现:
- 操作1:需要使用通用的增删改查操作
- 操作2:需要使用通用的增删改查操作
- 操作3:需要使用通用的增删改查操作
通用的增删改查操作如何实现?
- 手动使用PreparedStatement实现
- 使用dbutils.jar中QueryRunner类
conn.commit();
3.如果出现异常,则:
conn.rollback();
4.关闭资源
//方式1:手动关闭资源 方式2:DbUtils类的关闭方法
JDBCUtils.closeResource(..,...,...);
四、Dao及其子类
封装了针对于数据表的通用的操作
public abstract class BaseDAO<T> {
private Class<T> clazz = null;
{
//获取当前BaseDAO的子类继承的父类中的泛型,this在这里指的是子类
Type genericSuperclass = this.getClass().getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
Type[] typeArguments = paramType.getActualTypeArguments();//获取了父类的泛型参数
clazz = (Class<T>) typeArguments[0];//泛型的第一个参数
}
// 通用的增删改操作---version 2.0 (考虑上事务
public int update(Connection conn, String sql, Object... args) {
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.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 4.资源的关闭
JDBCUtils.closeResource(null, ps);
}
return 0;
}
// 通用的查询操作,用于返回数据表中的一条记录(version 2.0:考虑上事务
public T getInstance(Connection conn, String sql, Object... args) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
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();
if (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
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);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
// 通用的查询操作,用于返回数据表中的多条记录构成的集合(version 2.0:考虑上事务
public List<T> getForList(Connection conn, String sql, Object... args) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
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<T>();
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 {
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
//用于查询特殊值的通用的方法
public <E> E getValue(Connection conn,String sql,Object...args){
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
if(rs.next()){
return (E) rs.getObject(1);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
}
此接口用于规范针对于customers表的常用操作
public interface CustomerDAO {
/**
* 将cust对象添加到数据库中
* @param conn
* @param cust
*/
void insert(Connection conn,Customer cust);
/**
*
* 针对指定的id,删除表中的一条记录
* @param conn
* @param id
*/
void deleteById(Connection conn,int id);
/**
*
* 针对内存中的cust对象,去修改数据表中指定的记录
* @param conn
* @param cust
*/
void update(Connection conn,Customer cust);
/**
*
* 针对指定的id查询得到对应的Customer对象
* @param conn
* @param id
*/
Customer getCustomerById(Connection conn,int id);
/**
* 查询表中的所记录构成的集合
* @param conn
* @return
*/
List<Customer> getAll(Connection conn);
/**
*
* 返回数据表中的数据的条目数
* @param conn
* @return
*/
Long getCount(Connection conn);
/**
*
* 返回数据表中最大的生日
* @param conn
* @return
*/
Date getMaxBirth(Connection conn);
}
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 name = ?,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,name,email,birth from customers where id = ?";
Customer customer = getInstance(conn, sql,id);
return customer;
}
@Override
public List<Customer> getAll(Connection conn) {
String sql = "select id,name,email,birth from customers";
List<Customer> list = getForList(conn, sql);
return list;
}
@Override
public Long getCount(Connection conn) {
String sql = "select count(*) from customers";
return getValue(conn, sql);
}
@Override
public Date getMaxBirth(Connection conn) {
String sql = "select max(birth) from customers";
return getValue(conn, sql);
}
}
五、数据库连接池
1、C3P0
导入jar包:
测试连接的代码
public class C3P0Test {
//方式一
@Test
public void test1() throws PropertyVetoException, SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "com.mysql.jdbc.Driver" );
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true" );
cpds.setUser("root");
cpds.setPassword("12345");
//通过设置相关的参数,对数据库连接池进行管理
//设置初始时数据库连接池中的连接数
cpds.setInitialPoolSize(10);
Connection conn = cpds.getConnection();
System.out.println(conn);
//销毁连接池
// DataSources.destroy(cpds);
}
//方式二:使用配置文件
@Test
public void test2() throws SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource("hell3p0");
Connection conn = cpds.getConnection();
System.out.println(conn);
}
}
其中,配置文件定义在src下。名为:c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<named-config name="hell3p0">
<!-- 提供获取连接的4个基本信息 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true</property>
<property name="user">root</property>
<property name="password">12345</property>
<!-- 进行数据库连接池管理的基本信息 -->
<!-- 当数据库连接池中的连接数不够时,c3p0一次性向数据库服务器申请的连接数 -->
<property name="acquireIncrement">5</property>
<!-- c3p0数据库连接池中初始化时的连接数 -->
<property name="initialPoolSize">10</property>
<!-- c3p0数据库连接池维护的最少连接数 -->
<property name="minPoolSize">10</property>
<!-- c3p0数据库连接池维护的最多的连接数 -->
<property name="maxPoolSize">100</property>
<!-- c3p0数据库连接池最多维护的Statement的个数 -->
<property name="maxStatements">50</property>
<!-- 每个连接中可以最多使用的Statement的个数 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
2、DBCP
public class DBCPTest {
//方式一:不推荐
@Test
public void testGetConnection() throws SQLException {
//创建了DBCP的数据库连接池
BasicDataSource source = new BasicDataSource();
//设置基本信息
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true");
source.setUsername("root");
source.setPassword("12345");
//还可以设置其他涉及数据库连接池管理的相关属性
source.setInitialSize(10);
source.setMaxActive(10);
Connection conn = source.getConnection();
System.out.println(conn);
}
//方式二:使用配置文件
@Test
public void testGetConnection1() throws Exception{
Properties pros = new Properties();
//方式一
InputStream is =ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
//方式二
//FileInputStream is = new FileInputStream(new File("src/dbcp.properties.txt"));
pros.load(is);
DataSource sourse = BasicDataSourceFactory.createDataSource(pros);
Connection conn = sourse.getConnection();
System.out.println(conn);
}
}
其中,配置文件定义在src下:dbcp.properties
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
driverClassName=com.mysql.jdbc.Driver
username=root
password=12345
initialSize=10
3、Druid
public class DruidTest {
@Test
public void getConnection() throws Exception {
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
DataSource source = DruidDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
}
其中,配置文件定义在src下:druid.properties
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
driverClassName=com.mysql.jdbc.Driver
username=root
password=12345
initialSize=10
maxActive=10
六、DBUtils提供的jar包实现CRUD操作
commons-dbutils是Apache组织提供的一个开源JDBC工具类库,封装了对于数据库的增删改查操作
使用现成的jar中的QueryRunner测试增、删、改的操作
public class QueryRunnerTest {
//测试插入
@Test
public void testInsert() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "insert into customers(name,email,birth)values(?,?,?)";
int insertCount = runner.update(conn, sql, "小明","xiaoming@126.com","1997-09-08");
System.out.println("添加了" + insertCount + "条记录");
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, null);
}
}
/*
* 测试查询
* BeanHander:是ResultSetHandler接口的实现类,用于封装表中的一条记录。
*/
@Test
public void testQuery1(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
Customer customer = runner.query(conn, sql, handler, 23);
System.out.println(customer);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCUtls.closeResource(conn, null);
}
}
/*
* BeanListHandler:是ResultSetHandler接口的实现类,用于封装表中的多条记录构成的集合。
*/
@Test
public void testQuery2() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id < ?";
BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);
List<Customer> list = runner.query(conn, sql, handler, 23);
list.forEach(System.out::println);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, null);
}
}
/*
* MapHander:是ResultSetHandler接口的实现类,对应表中的一条记录。
* 将字段及相应字段的值作为map中的key和value
*/
@Test
public void testQuery3(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
MapHandler handler = new MapHandler();
Map<String, Object> map = runner.query(conn, sql, handler, 23);
System.out.println(map);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, null);
}
}
/*
* MapListHander:是ResultSetHandler接口的实现类,对应表中的多条记录。
* 将字段及相应字段的值作为map中的key和value。将这些map添加到List中
*/
@Test
public void testQuery4(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id < ?";
MapListHandler handler = new MapListHandler();
List<Map<String, Object>> list = runner.query(conn, sql, handler, 23);
list.forEach(System.out::println);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, null);
}
}
/*
* ScalarHandler:用于查询特殊值
*/
@Test
public void testQuery5(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select count(*) from customers";
ScalarHandler handler = new ScalarHandler();
Long count = (Long) runner.query(conn, sql, handler);
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, null);
}
}
@Test
public void testQuery6(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select max(birth) from customers";
ScalarHandler handler = new ScalarHandler();
Date maxBirth = (Date) runner.query(conn, sql, handler);
System.out.println(maxBirth);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, null);
}
}
/*
* 自定义ResultSetHandler的实现类
*/
@Test
public void testQuery7(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>(){
@Override
public Customer handle(ResultSet rs) throws SQLException {
// System.out.println("handle");
// return null;
// return new Customer(12, "成龙", "Jacky@126.com", new Date(234324234324L));
if(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer customer = new Customer(id, name, email, birth);
return customer;
}
return null;
}
};
Customer customer = runner.query(conn, sql, handler,23);
System.out.println(customer);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, null);
}
}
}
使用dbutils.jar包中的DbUtils工具类实现连接等资源的关闭
public static void closeResource1(Connection conn,Statement ps,ResultSet rs){
// try {
// DbUtils.close(conn);
// } catch (SQLException e) {
// e.printStackTrace();
// }
// try {
// DbUtils.close(ps);
// } catch (SQLException e) {
// e.printStackTrace();
// }
// try {
// DbUtils.close(rs);
// } catch (SQLException e) {
// e.printStackTrace();
// }
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(ps);
DbUtils.closeQuietly(rs);
}
注意两个方法的区别
public static void closeQuietly(Connection conn) {
try {
close(conn);
} catch (SQLException var2) {
}
}
public static void close(Connection conn) throws SQLException {
if (conn != null) {
conn.close();
}
}