1.数据的持久化
- 持久化(persistense):把数据保存在可掉电式存储设备中以供之后使用,大多数情况下,特别式企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以固化,持久化的实现过程大多通过各种关系数据库来实现。
- 持久化的主要应用式将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML文件中。
2.数据的持久化
JDBC(java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)
好处:
- 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行失sql语句,获得结果)
- 面向数据的API(Java Driver API),供开发商开发数据库驱动程序用
3.数据库的连接
将数据库需要的4个基本信息声明在配置文件中,通过读取配置文件的方式,获取连接
这种方式的好处:
1.实现了数据与代码的分离。实现了解耦
2.如果需要修改配置文件信息,可以避免程序重新打包
@Test
public void testConnection5() throws Exception {
//1.读取配置文件的4个基本信息(这里用的类加载器,推荐使用系统加载器)
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(is);
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClass = properties.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
创建jdbc.properties文件,此配置文件声明在Module的src下:
user=root
password=123456
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
driverClass=com.mysql.jdbc.Driver
4.使用PreparedStatement操作数据库表
4.1 Statement操作数据表的弊端
- 问题一:存在拼串操作,繁琐
- 问题二:存在SQL注入问题,如
select user,password from user_table where user='a' or 1 = 'and password=' or '1' = '1'
4.2 使用PreparedStatement操作数据表
PreparedStatement是Statement的子接口,能够预编译SQL语句。
除了解决Statement的拼串、sql问题之外,PreparedStatement还有哪些好处呢?
1.PreparedStatement操作Blob的数据,而Statement做不到
2.PreparedStatement可以实现更高效的批量操作。
使用PreparedStatement实现通用的增删改的方法(考虑事务)
/**
* 通用的增删改操作 ---Version 2.0 (考虑数据库事务)
*/
public int update(Connection conn, String sql,Object ...args) {
PreparedStatement ps = null;
try {
//预编译sql语句
ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++){
ps.setObject(i + 1, args[i]);
}
//执行
return ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭连接
JDBCUtils.closeResource(null,ps);
}
return 0;
}
使用PreparedStatement实现通用的查询方法(考虑事务)
返回一个对象:
/**
* 通用的查询操作,用于返回数据表中的一条记录(version 2.0 :考虑上事务)
*/
public <T> T queryComForOrder(Connection conn,Class<T> clazz,String sql,Object ...args) throws Exception {
//2.预编译sql语句
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++){
ps.setObject(i + 1,args[i]);
}
//3.执行查询语句
ResultSet rs = ps.executeQuery();
//获取结果集元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int count = rsmd.getColumnCount();
if (rs.next()){
T t = clazz.newInstance();
for (int i = 0; i < count; i++){
//获取列的值
Object columnValue = rs.getObject(i + 1);
//获取列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
//利用反射设置对象的值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
return t;
}
JDBCUtils.closeResource(null,ps,rs);
return null;
}
返回多个对象构成的集合:
/**
* 通用的查询操作,用于返回数据表中的多条记录(version 2.0 :考虑上事务)
*/
public <T> List<T> queryComForTable(Connection conn,Class<T> clazz, String sql, Object ...args){
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList<T> list = null;
try {
//2.预编译
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++){
ps.setObject(i + 1,args[i]);
}
//执行
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int count = rsmd.getColumnCount();
//创建集合对象
list = new ArrayList<>();
while (rs.next()){
T t = clazz.newInstance(); //用到了反射
for (int i = 0; i < count; i++){
//取得列的值
Object columnValue = rs.getObject(i + 1);
//取得列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
//利用反射设置
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
4.3 两种思想与两种技术
两种思想
- 面向接口编程的思想
- ORM编程思想 (object relational mapping):
一个表对应一个java类
表中的一条记录对应java的一个对象
表中的一个字段对应java的一个属性
两种技术
- 使用结果集的元数据:ResultSetMetaDate
getColumnCount():获取列数
getColumnLabel():获取列的别名
针对表的字段名与类的属性名不相同的情况:
1.必须声明sql时,使用类的属性名来命名字段的别名
2.使用ResultSetMetaDate时,需要使用getColumnLabel()替换getColumnName(),获取列的别名
说明:如果sql中没有取别名,getColumnLabel()获取的就是列名
- 反射的使用
①创建对应的运行时类的对象
②在运行时,动态的调用指定的运行时类的属性
4.4 使用PreparedStatement操作Blob字段
写入操作方法:setBlob(InputStream is)
读取操作方法:
Blob blob = blob.getBlob(int index)
InputStream is = blob.getBinaryStream();
具体的insert
/**
* 向数据表Customers中插入一条Blob数据
* @throws Exception
*/
@Test
public void BlobTest() throws Exception {
//1.获取连接
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,"zhang@qq.com");
ps.setObject(3,"1990-02-12");
//处理Blob数据
FileInputStream fis = new FileInputStream(new File("1.jpg"));
ps.setBlob(4,fis);
ps.execute();
JDBCUtils.closeResource(conn,ps);
}
具体的query
@Test
public void test2() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql = "select id,name,email,birth,photo from customers where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,"16");
ResultSet 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);
Customer customer = new Customer(id,name,email,birth);
System.out.println(customer);
//具体的query,这里使用的是Label,也可以使用index
Blob photo = rs.getBlob("photo");
InputStream is = photo.getBinaryStream();
FileOutputStream fos = new FileOutputStream("zhuyin.jpg");
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
is.close();
fos.close();
}
JDBCUtils.closeResource(conn,ps,rs);
}
注意:如果在指定了相关的Blob类型后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数:max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。
4.5 使用PreparedStatement实现批量插操作
update、delete本身就具有批量操作的效果。
此时的批量操作,主要是指批量插入。使用PreparedStatement如何实现高效的批量插入?
1.addBatch()、executeBatch、clearBatch()
2.mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理支持。?rewriteBatchedStatements=true写在配置文件的url后面
3.更新新的驱动:mysql-connector-java-5.1.37-bin.jar
4. 设置不允许自动提交
/**
* 批量插入:设置不允许自动提交
*/
@Test
public void testBatchInsert4() throws Exception {
Connection conn = JDBCUtils.getConnection();
//设置不允许自动提交数据
conn.setAutoCommit(false);
String sql = "insert into goods(name) values(?)";
PreparedStatement 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();
}
ps.execute();
}
//提交数据
conn.commit();
JDBCUtils.closeResource(conn,ps);
}
4.6 Statement与PreparedStatement的异同?
① 指出二者的关系? 接口与子接口的关系。
②开发中,PreparedStatement替换Statement。
③PreparedStatement能够预编译Sql语句,预编译的语句有可能被重复利用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来。下次再次调Stateme用时只要是相同的预编译语句就不需要编译,只需要将参数直接传入编译过的执行代码就会获得执行。而Statement每执行一次就要对传入的语句编译一次。
④PreparedStatement可以防止SQL注入。
5. 数据库事务
事务:一组逻辑操作单元,使数据从一种状态转换到另一种状态
> 一组逻辑操作单元:一个或多个DML操作。
5.1 事务处理的原则
保证所有事务都作为一个工作单元来执行,即使出了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么修改就永久的保存下来;要么数据库管理系统将放弃所作修改,将整个事务**回滚(rollback)**到最初状态。
说明:
数据一旦提交,就不可回滚
哪些操作会导致数据的自动提交?
DDL操作一旦执行,都会自动提交。
set autocommit = false 的方式对DDL操作失效。
DML默认情况下,一旦执行,都会自动提交。
我们可以通过set autocommit = false的方式取消DML操作的自动提交。
在关闭数据库连接的时候,会自动的提交数据。
5.2 事务的代码体现
/**
* * **********************考虑数据库事务后的操作**************************************
*
*/
@Test
public void testUpdate2() {
Connection conn = null;
try {
//建立连接
conn = JDBCUtils.getConnection();
//取消数据的自动提交
conn.setAutoCommit(false);
//执行数据库操作
String sql = "update user_table set balance = balance - 100 where user = ?";
update(conn,sql,"AA");
//模拟网络异常
System.out.println(100 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn,sql2,"BB");
//提交数据
conn.commit();
System.out.println("转账成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("转账失败");
//回滚数据
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
try {
//修改其未自动提交数据
//主要针对于使用数据库连接池的使用
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
//关闭连接
JDBCUtils.closeResource(conn,null);
}
}
5.3 事务的ACID属性以及隔离级别
事务的ACID属性:
1.原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
2.一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态
3.隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能相互干扰
4.持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中的数据的改变就是永久性的,接下来其他操作和数据库故障不应该对其有任何用影响
数据库并发的三种问题:
脏读:T1读取了已经被T2更新但还没有提交的字段,之后若T2回滚,T1读取的内容就是临时且无效的
不可重复读:T1读取一个字段,然后T2更新的此字段,之后T1再读取此字段,值就不同了
幻读:T1从一个表中读取一个字段,然后T2在表中插入了一些新的行。之后如果T1再次读取同一个表,就会多出几行。
四种隔离级别:
READ UNCOMMITTED(读未提交) -----上述三种情况都会出现
READ COMMITTED(读已提交) -----不会出现脏读
REPEATABLE READ(可重复读) -----不会出现脏读和重复读
SERIALIZABLE(串行化) -----不会出现上述三种情况
6. DAO及其子类
封装了对于数据表的通用操作
6.1 DAO代码实现举例
BaseDAO.java
/**
* DAO:data(base) access object
* 封装了对于数据表的通用操作
*
* @author Yee
* @create 2023-07-06 17:06
*/
public abstract class BaseDao {
/**
* 通用的增删改操作 ---Version 2.0 (考虑数据库事务)
* @param conn
* @param sql
* @param args
* @return
*/
public int update(Connection conn, String sql, Object ...args) {
PreparedStatement ps = null;
try {
//预编译sql语句
ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++){
ps.setObject(i + 1, args[i]);
}
//执行
return ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭连接
JDBCUtils.closeResource(null,ps);
}
return 0;
}
/**
* 通用的查询操作,用于返回数据表中的一条记录(version 2.0 :考虑上事务)
* @param conn
* @param clazz
* @param sql
* @param args
* @param <T>
* @return
* @throws Exception
*/
public <T> T queryComForOrder(Connection conn,Class<T> clazz,String sql,Object ...args) throws Exception {
//2.预编译sql语句
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++){
ps.setObject(i + 1,args[i]);
}
//3.执行查询语句
ResultSet rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int count = rsmd.getColumnCount();
if (rs.next()){
T t = clazz.newInstance();
for (int i = 0; i < count; i++){
//获取列的值
Object columnValue = rs.getObject(i + 1);
//获取列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
//利用反射设置对象的值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
return t;
}
JDBCUtils.closeResource(null,ps,rs);
return null;
}
/**
* 通用的查询操作,用于返回数据表中的多条记录(version 2.0 :考虑上事务)
* @param clazz
* @param sql
* @param args
* @param <T>
* @return
*/
public <T> List<T> queryComForTable(Connection conn,Class<T> clazz, String sql, Object ...args){
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList<T> list = null;
try {
//2.预编译
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++){
ps.setObject(i + 1,args[i]);
}
//执行
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int count = rsmd.getColumnCount();
//创建集合对象
list = new ArrayList<>();
while (rs.next()){
T t = clazz.newInstance();
for (int i = 0; i < count; i++){
//取得列的值
Object columnValue = rs.getObject(i + 1);
//取得列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
//利用反射设置
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null,ps,rs);
}
return null;
}
/**
* 用于查询特殊值的通用方法
* @param conn
* @param sql
* @param args
* @param <E>
* @return
*/
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;
}
}
CustomerDAO.java
/**
* 此接口用于规范对于customers表的常用操作
*
* @author Yee
* @create 2023-07-06 17:17
*/
public interface CustomerDAO {
/**
* 将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) throws Exception;
/**
* 查询表中的所有记录构成的集合
* @param conn
* @return
*/
List<Customer> getAll(Connection conn);
/**
* 返回数据表中数据的条目数
* @param conn
* @return
*/
Long getCount(Connection conn);
/**
* 返回数据表中最大的生日
*/
Date getMaxBirth(Connection conn);
}
CustomerDAOImpl.java
public class CustomerDAOImpl extends BaseDao 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) throws Exception {
String sql = "select name,birth,id,email from customers where id = ?";
Customer customer = queryComForOrder(conn, Customer.class, sql, id);
return customer;
}
@Override
public List<Customer> getAll(Connection conn) {
String sql = "select id,name,email,birth from customers";
List<Customer> list = queryComForTable(conn, Customer.class, 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);
}
}
6.2 DAO总结
- 获取数据库的连接
//方式一 : 手动获取连接 方式二 : 数据库连接池
Connection conn = JDBCUtils.getConnection();
- 如下的多个DML操作,作为一个事务出现:
//通用的增删改查如何实现
/*
方式一:手动使用PreparedStatement实现
方式二:使用dbutils.jar中QueryRunner类实现
*/
操作一:需要使用通用的增删改查操作
操作二:需要使用通用的增删改查操作
操作三:需要使用通用的增删改查操作
...
conn.commit();
- 如果出现异常,则:
conn.rollback();
- 关闭资源
/*
方式一:手动关闭资源
方式二:DbUtils类的关闭方法
*/
JDBCUtils.closeResource(...,...,...);
7. 数据库连接池技术
传统连接存在的问题:
- 普通的]DBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将
Connection加载到内存中,再验证用户名和密码(得花费0.05s~
1s的时间)。需要数据库连接的时候,就向数据库要一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到好的重复利用。若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,重的甚至会造成服务器的崩溃。 - 对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。( 回忆:何为java的内存泄漏 ?)
- 这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内
存泄漏,服务器崩溃。
7.1 数据库连接池技术的优点
- 资源重用
由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。 - 更快的系统反应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销从而减少了系统的响应时间。 - 新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源。 - 统一的连接管理,避免数据库连接泄漏
在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露
通俗来讲,即:
- 提高程序的响应速度(减少了创建连接相应的时间)
- 降低了资源的消耗(可以重复使用已经提供好的连接)
- 便于连接的管理
7.2 数据库连接池具体实现方式
- DBCP:是Apache提供的数据库连接池。tomcat服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernates3已经不再支持。
- C3P0:是一个开源组织提供的数据库连接池,速度相对较慢,稳定性还可以,hibernate官方推荐使用。
- Druid:是阿里提供的数据库连接池,据说是集DBCP、C3P0、Proxool优点于一身的数据库连接池,但是速度不确定是否有BoneCP快。
7.2.1 C3P0数据库连接池技术
导入jar包: c3p0-0.9.1.2.jar
/**
* 使用配置文件
*/
@Test
public void testGetConnection2() throws SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
Connection conn = cpds.getConnection();
System.out.println(conn);
}
其中,配置文件定义在src下,名为c3p0-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>
<named-config name="helloc3p0">
<!-- 提供获取连接的4个基本信息-->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
<property name="user">root</property>
<property name="password">123456</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>
<!-- c3p0数据库连接池中每个连接最多使用的Statement的个数-->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
7.2.2 DBCP数据库连接池技术
导入jar包:commons-dbcp-1.4.jar
commons-pool-1.5.5.jar
/**
* 使用配置文件(推荐)
*/
@Test
public void testGetConnection1() throws Exception {
Properties pros = new Properties();
//方式一
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
pros.load(is);
//方式二
// FileInputStream is = new FileInputStream(new File("dbcp.properties"));
DataSource dataSource = BasicDataSourceFactory.createDataSource(pros);
Connection conn = dataSource.getConnection();
System.out.println(conn);
}
其中,配置文件定义在src下,名为dbcp.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///test
username=root
password=123456
initialSize=10
7.2.3 Druid数据库连接池技术
导入jar包:druid-1.1.10.jar
public void testConnection() 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:///test
username=root
password=123456
driverClass=com.mysql.jdbc.Driver
initialSize=10
maxActive=10
7.3 使用QueryRunner实现增删改查操作(DBUtils应用)
测试插入
/**
* 测试的插入操作
* @throws SQLException
*/
@Test
public void testInsert() throws SQLException {
QueryRunner runner = new QueryRunner();
Connection conn = JDBCUtils1.getConnectionByDBCP();
String sql = "insert into customers(name,email,birth) values(?,?,?)";
int insertCount = runner.update(conn, sql, "蔡徐坤", "caixukun@126.com", "1997-09-08");
System.out.println("添加了" + insertCount + "条记录");
JDBCUtils1.closeResource(conn,null);
}
测试查询
/**
* 测试查询:单条数据
* BeanHandler 是 ResultSetHandler接口的实现类,用于封装表中一条记录
*
*/
@Test
public void testQuery() throws SQLException {
QueryRunner runner = new QueryRunner();
Connection conn = JDBCUtils1.getConnectionByDBCP();
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);
JDBCUtils1.closeResource(conn,null);
}
/*
Query:
BeanHandler() \ BeanListHandler() --- 查询表中一条以及多条记录
MapHandler() \ MapListHandler() --- 封装表中一条或多条记录 key - value 对
ScalarHandler() ----- 用于查询特殊值
*/
使用Dbutils.jar包中提供的DbUtils工具类,实现资源的关闭
/**
*
* 使用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);
}