数据库连接池
1、概念
每次访问数据库都要向系统底层获取连接对象,访问结束又释放连接对象,效率很低
本质上是一个存放数据库连接对象的容器(集合)
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户访问数据库时,从容器中获取连接对象,访问结束会将其放回容器
好处:
节约资源
用户访问高效
使用:
标准接口 javax.sql.DataSource
方法:
获取连接 Connection getConnection();
归还连接 如果连接对象是从数据库连接池中获取的,
那么调用Connection.close()就不会释放连接了,而是归还连接
一般我们不实现,由数据库厂商实现,厂商一般也使用一些开源的数据库连接池实现技术
数据库连接池实现技术:
C3P0
Druid 阿里巴巴
2、C3P0的使用
步骤:
(1)导入两个jar包 c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar
复制到lib文件夹下,然后右键Add As Library…
还要导入数据库的驱动jar包 mysql-connector-java-5.1.37-bin.jar
然后右键Add As Library…
(2)定义配置文件
名称:必须为c3p0.properties或c3p0-config.xml
路径:直接将文件放在src目录下
会自动加载
这里将已有的配置文件复制到src目录下,修改其中的参数
(3)创建核心对象 数据库连接池对象ComboPooledDataSource
(4)获取连接getConnection();
c3p0-config.xml
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db3</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<!-- 初始化神申请的连接数量 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">8</property>
<property name="checkoutTimeout">1000</property>
</named-config>
</c3p0-config>
public class Demo02C3P0 {
public static void main(String[] args) throws SQLException {
//创建数据库连接池对象
//空参使用配置文件中的默认配置,传参传的是配置文件中不同配置的名称,使用对应名称的配置
DataSource ds = new ComboPooledDataSource();
//获取连接对象
Connection conn = ds.getConnection();
//会先打印一些日志信息
System.out.println(conn);//com.mchange.v2.c3p0.impl.NewProxyConnection@2dde1bff [wrapping: com.mysql.jdbc.JDBC4Connection@15bbf42f]
if (conn != null) {
conn.close();
}
}
}
3、Druid的使用
步骤:
(1)导入jar包 druid-1.0.9.jar
没有导入数据库驱动jar包的话也要导入
(2)定义配置文件
是properties格式的
可以叫任意名称,放在任意目录下
需要手动加载
这里将已有的配置文件复制到src目录下,修改其中的参数
(3)加载配置文件
Properties pro = new Properties();
InputStream is = Demo03Druid.class.getClassLoader().getResourceAsStream();
pro.load(is);
DataSource ds = DataSourceFactory.getDataSource(pro);
(4)获取数据库连接池对象
通过数据库工厂类来获取 DruidDataSourceFactory
(5)获取连接
getConnection();
public class Demo03Druid {
public static void main(String[] args) throws Exception {
//加载配置文件
Properties pro = new Properties();
//获取文件对应的流
InputStream is = Demo03Druid.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//获取数据库连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
Connection conn = ds.getConnection();
System.out.println(conn);//com.mysql.jdbc.JDBC4Connection@7a8c8dcf
if (conn != null) {
conn.close();
}
}
}
4、使用Druid工具类
druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db2
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
Druid连接池的工具类
使用静态代码块加载配置文件,初始化连接池对象
提供的方法:
获取连接
释放资源
获取连接池
public class DruidUtils {
//定义成员变量 数据库连接池对象
private static DataSource ds;
//静态代码块,在类加载时执行,且只执行这一次
static {
try {
//创建Properties对象
Properties pro = new Properties();
//获取配置文件对应的输入流
InputStream is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//加载配置文件
pro.load(is);
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接对象
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//释放资源
public static void close(Statement stat, Connection conn) {
try {
if (stat != null) {
stat.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(ResultSet rs, Statement stat, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (stat != null) {
stat.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
//获取连接池对象
public static DataSource getDataSource() {
return ds;
}
}
测试类
public class Demo04DruidUtils {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement stat = null;
try {
//注册驱动可以省略在MySQL5之后
//1、获取连接对象
conn = DruidUtils.getConnection();
//2、定义sql
String sql = "insert into account values(null, ?, ?);";
//3、获取执行sql的PreparedStatement对象
stat = conn.prepareStatement(sql);
//4、给sql的参数赋值
stat.setString(1, "赵六");
stat.setDouble(2, 3000);
//5、执行sql
int count = stat.executeUpdate();
//6、处理结果
System.out.println(count);
if (count > 0) {
System.out.println("添加成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DruidUtils.close(stat, conn);
}
}
}
5、Spring JDBC
Spring框架对JDBC的简单封装,其提供了一个JDBCTemplate对象简化JDBC的开发
步骤:
(1)导入jar包 commons-logging-1.2.jar
spring-beans-5.0.0.RELEASE.jar
spring-core-5.0.0.RELEASE.jar
spring-jdbc-5.0.0.RELEASE.jar
spring-tx-5.0.0.RELEASE.jar 并右键Add As Library…
(2)创建JDBCTemplate对象,传递的参数为数据源DataSource对象
JDBCTemplate j = new JDBCTemplate(ds);
(3)使用JDBCTemplate的方法完成CRUD增删改查的操作
update();执行DML语句,进行增删改
queryForMqp();查询,将结果集封装为Map集合 列名作为key,值作为value
该方法只能将只有一条记录的结果集封装为Map集合
queryForList();查询,将结果集封装为List集合
将结果集的每条记录封装为Map集合,
然后再将所有Map集合封装为List集合
query();查询,将结果集封装为JavaBean对象,也就是类对象
参数为sql语句和RowMapper接口
RowMapper接口可以自己重写其中的方法,
但一般使用其实现类BeanPropertyRowMapper,
将对象类型和对应字节码文件对象传递进去,
可以完成JavaBean对象的自动封装
List list =
template.query(sql, new BeanPropertyRowMapper(Emp.class));
queryForObject();查询,将结果集封装为对象,一般是基本数据类型的对象
一般用于聚合函数的查询
public class Demo01JDBCTemplate {
public static void main(String[] args) {
//创建JDBCTemplate对象
JdbcTemplate template = new JdbcTemplate(DruidUtils.getDataSource());
//定义sql
String sql = "update account set balance = ? where name = ?;";
//调用JdbcTemplate的方法 传递的参数值与上面的?一一对应
int count = template.update(sql, 5000, "赵六");
System.out.println(count);
if (count > 0) {
System.out.println("修改成功!");
}
}
}
6、练习
emp表
(1)修改一号员工的工资为10000
(2)添加一条记录
(3)删除刚才添加的记录
(4)查询id为1001的记录,封装为Map集合
(5)查询所有记录,封装为List集合
(6)查询所有记录,封装为Emp对象的List集合
(7)查询总记录数
员工类
public class Emp {
private Integer id;
private String ename;
private Integer job_id;
private Integer mgr;
private Date joindate;
private Double salary;
private Double bonus;
private Integer dept_id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Integer getJob_id() {
return job_id;
}
public void setJob_id(Integer job_id) {
this.job_id = job_id;
}
public Integer getMgr() {
return mgr;
}
public void setMgr(Integer mgr) {
this.mgr = mgr;
}
public Date getJoindate() {
return joindate;
}
public void setJoindate(Date joindate) {
this.joindate = joindate;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Double getBonus() {
return bonus;
}
public void setBonus(Double bonus) {
this.bonus = bonus;
}
public Integer getDept_id() {
return dept_id;
}
public void setDept_id(Integer dept_id) {
this.dept_id = dept_id;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", ename='" + ename + '\'' +
", job_id=" + job_id +
", mgr=" + mgr +
", joindate=" + joindate +
", salary=" + salary +
", bonus=" + bonus +
", dept_id=" + dept_id +
'}';
}
}
测试类
public class Demo02Pra {
private JdbcTemplate template = new JdbcTemplate(DruidUtils.getDataSource());
//使用Junit 让方法独立执行
//修改一号员工的工资为10000
@Test
public void update() {
String sql = "update emp set salary = ? where id = ?;";
int count = template.update(sql, 10000, 1001);
System.out.println(count);
}
//添加一条记录
@Test
public void insert() {
String sql = "insert into emp values(?, ?, ?, ?, ?, ?, ?, ?)";
int count = template.update(sql, 1015, "吴承恩", 1, null, "2001-01-01", 80000.0, null, 10);
System.out.println(count);
}
//删除刚才添加的记录
@Test
public void delete() {
String sql = "delete from emp where id = ?;";
int count = template.update(sql, 1015);
System.out.println(count);
}
//查询id为1001的记录,封装为Map集合 列名作为key,值作为value 该方法只能将只有一条记录的结果集封装为Map集合
@Test
public void selectToMap() {
String sql = "select * from emp where id = ?;";
Map<String, Object> map = template.queryForMap(sql, 1001);
System.out.println(map);//{ID=1001, ENAME=孙悟空, JOB_ID=4, MGR=1004, JOINDATE=2000-12-17, SALARY=10000.00, BONUS=null, DEPT_ID=20}
}
//查询所有记录,封装为List集合 将结果集的每条记录封装为Map集合,然后再将所有Map集合封装为List集合
@Test
public void selectToList() {
String sql = "select * from emp;";
List<Map<String, Object>> list = template.queryForList(sql);
System.out.println(list);
}
//查询所有记录,封装为Emp对象的List集合
//自己重写方法
@Test
public void selectToEmpList() {
String sql = "select * from emp;";
List<Emp> list = template.query(sql, new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet rs, int i) throws SQLException {
Emp emp = new Emp();
emp.setId(rs.getInt("id"));
emp.setEname(rs.getString("ename"));
emp.setJob_id(rs.getInt("job_id"));
emp.setMgr(rs.getInt("mgr"));
emp.setJoindate(rs.getDate("joindate"));
emp.setSalary(rs.getDouble("salary"));
emp.setBonus(rs.getDouble("bonus"));
emp.setDept_id(rs.getInt("dept_id"));
return emp;
}
});
System.out.println(list);
}
//使用别人写好的方法
//BeanPropertyRowMapper已经实现了RowMapper接口
@Test
public void selectToEmpList2() {
String sql = "select * from emp;";
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
System.out.println(list);
}
//查询总记录数
@Test
public void selectCount() {
String sql = "select count(id) from emp;";
long total = template.queryForObject(sql, Long.class);
System.out.println(total);
}
}