博客 |
---|
JavaWeb |
JDBC连接池概念
概念:其实就是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
好处:
1. 节约资源
2. 用户访问高效
JDBC连接池实现
1.标准接口:DataSource
方法:
- 获取连接:getConnection()。
- 归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了,是归还连接。
一般我们不去实现它,有数据库厂商来实现
- C3P0:数据库连接池技术。
- DBCP:数据库连接池技术。
- Druid:数据库连接池实现技术,由阿里巴巴提供的。
2.C3P0:数据库连接池
1.C3P0使用步骤
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
1. 导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar,不要忘记导入数据库驱动jar包。
2. 定义配置文件:
名称: c3p0.properties 或者 c3p0-config.xml。(会自动加载)
路径:直接将文件放在src目录下即可。
3. 创建核心对象 数据库连接池对象 ComboPooledDataSource。
4. 获取连接:getConnection。
2.C3P0代码实现
/**
* c3p0的演示
*/
public class C3P0Demo1 {
public static void main(String[] args) throws SQLException {
//1.创建数据库连接池对象
DataSource ds = new ComboPooledDataSource();//自动加载c3p0-config.xml配置文件
//2. 获取连接对象
Connection conn = ds.getConnection();
//3. 打印
System.out.println(conn);
//测试最大连接数(正在连接的数),超过了最大连接数,就会报错。
//testMaxConnectionNum();
}
public static void testMaxConnectionNum() throws SQLException {
// 1.1 获取DataSource,使用指定名称配置
DataSource ds = new ComboPooledDataSource("otherc3p0");
//2.获取连接
for (int i = 1; i <= 10 ; i++) {
Connection conn = ds.getConnection();
System.out.println(i+":"+conn);
}
}
}
3.C3P0工具类
/**
* C3P0连接池的工具类
*/
public class JDBCUtils3 {
private static DataSource cpds = new ComboPooledDataSource();//自动加载c3p0-config.xml配置文件
/**
* 获取连接
*
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return cpds.getConnection();
}
/**
* 获取连接池
*
* @return
*/
public static DataSource getDataSource() {
return cpds;
}
/**
* 增删改有关可以关闭的对象Statement,Connection。
* @param stmt
* @param conn
*/
public static void close(Statement stmt, Connection conn) {
/*if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}*/
close(null,stmt,conn);
}
/**
* 查有关可以关闭的对象ResultSet,Statement,Connection。
* @param resultSet
* @param stmt
* @param conn
*/
public static void close(ResultSet resultSet, Statement stmt, Connection conn) {
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
c3p0-config.xml配置文件
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db4</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/db3</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>
3.DBCP:数据库连接池
1.DBCP使用步骤
DBCP(DataBase connection pool),数据库连接池。是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件。由于建立数据库连接是一个非常耗时耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后再放回去。
第一步:引入DBCP连接池的jar包.
单独使用dbcp需要2个包:commons-dbcp.jar,commons-pool.jar
第二步:编写DBCP代码:
手动设置参数:
配置文件设置参数:
2.DBCP代码实现
public class DBCPDemo {
public static void main(String[] args) throws Exception {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
Properties properties = new Properties();
try{
properties.load(new FileInputStream("src/dbcpconfig.properties"));
DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
// 获得连接:
conn = dataSource.getConnection();
// 编写SQL:
String sql = "select * from category";
// 预编译SQL:
stmt = conn.prepareStatement(sql);
// 执行SQL:
rs = stmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("cid")+" "+rs.getString("cname"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs,stmt, conn);
}
}
4.Druid:数据库连接池
1.Druid使用步骤
1. 导入jar包 druid-1.0.9.jar
2. 定义配置文件:
是properties形式的。
可以叫任意名称,可以放在任意目录下。
3. 加载配置文件。Properties
4. 获取数据库连接池对象:通过工厂来来获取 DruidDataSourceFactory。
5. 获取连接:getConnection。
2.Druid代码实现
/**
* Druid演示
*/
public class DruidDemo {
public static void main(String[] args) throws Exception {
//1.导入jar包
//2.定义配置文件
//3.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//4.获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
//5.获取连接
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
3.Druid工具类
/**
* Druid连接池的工具类
*/
public class JDBCUtils {
//1.定义成员变量 DataSource
private static DataSource ds;
static {
try {
//1.加载配置文件
Properties pro = new Properties();
pro.load(JDBCUtils2.class.getClassLoader().getResourceAsStream("druid.properties"));
//2.获取DataSource
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 DataSource getDataSource() {
return ds;
}
/**
* 增删改相关可以关闭的对象Statement,Connection。
* @param stmt
* @param conn
*/
public static void close(Statement stmt, Connection conn) {
/* if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();//归还连接
} catch (SQLException e) {
e.printStackTrace();
}
}*/
close(null, stmt, conn);
}
/**
* 查相关可关闭的对象ResultSet,Statement,Connection。
* @param rs
* @param stmt
* @param conn
*/
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();//归还连接
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
druid.properties配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///mydatabase
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000
SpringJDBC
Spring框架对JDBC的简单封装,提供了一个JDBCTemplate对象简化JDBC的开发。
其实Spring框架对JDBC简化了传参,以及对结果集的封装。
1.SpringJDBC使用步骤
1. 导入jar包
2. 创建JdbcTemplate对象,依赖于数据源DataSource。
JdbcTemplate template = new JdbcTemplate(ds);
3. 调用JdbcTemplate的方法来完成CRUD的操作。
update():执行DML语句,增、删、改语句。
queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合。
注意:这个方法查询的结果集长度只能是1。
queryForList():查询结果将结果集封装为list集合。
注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中。
query():查询结果,将结果封装为JavaBean对象。
query的参数:RowMapper
一般我们使用BeanPropertyRowMapper实现类,可以完成数据到JavaBean的自动封装。
new BeanPropertyRowMapper<类型>(类型.class)。
queryForObject:查询结果,将结果封装为对象。
一般用于聚合函数的查询。
2.SpringJDBC练习
练习的实体类
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 +
'}';
}
}
代码演示
1.修改1号数据的cname为动物园门票。
JdbcTemplate jt = new JdbcTemplate(JDBCUtils.getDataSource());
@Test
public void test1() {
//1.修改1号数据的cname为动物园门票。
//String sql = "update tab_category set cname = ? where cid = ?";
String sql = "update tab_category set cname = ?";
//返回的是影响的行数
int row = jt.update(sql, "动物园门票");
System.out.println(row);//8
}
2.添加一条记录。
JdbcTemplate jt = new JdbcTemplate(JDBCUtils.getDataSource());
@Test
public void test2() {
//2.添加一条记录。
String sql = "insert into tab_category values(?,?)";
//返回的是影响的行数
int row = jt.update(sql, 9, "太空游");
System.out.println(row);
}
3.删除刚才添加的记录。
JdbcTemplate jt = new JdbcTemplate(JDBCUtils.getDataSource());
@Test
public void test3() {
//3.删除刚才添加的记录。
String sql = "delete from tab_category where cid = ?";
//返回的是影响的行数
int row = jt.update(sql, 9);
System.out.println(row);
}
4. 查询id为1的记录,将其封装为Map集合。
JdbcTemplate jt = new JdbcTemplate(JDBCUtils.getDataSource());
@Test
public void test4() {
//4. 查询id为1的记录,将其封装为Map集合。
//String sql = "select * from tab_category where cid = ?";
//报错:org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 1, actual 2
//返回Map集合的长度只能是1。
String sql = "select * from tab_category where cid = ? or cid = ?";
Map<String, Object> map = jt.queryForMap(sql, 1, 2);
System.out.println(map);
}
5. 查询所有记录,将其封装为List。
JdbcTemplate jt = new JdbcTemplate(JDBCUtils.getDataSource());
@Test
public void test5() {
//5. 查询所有记录,将其封装为List。
String sql = "select * from tab_category";
List<Map<String, Object>> lists = jt.queryForList(sql);
for (Map<String, Object> map : lists) {
System.out.println(map);
}
}
结果:
{cid=1, cname=门票}
{cid=2, cname=酒店}
{cid=3, cname=香港车票}
{cid=4, cname=处境游}
{cid=5, cname=国内游}
{cid=6, cname=港澳游}
{cid=7, cname=抱团定制}
{cid=8, cname=全球自由行}
6. 查询所有记录,将其封装为tab_category对象的List集合。
JdbcTemplate jt = new JdbcTemplate(JDBCUtils.getDataSource());
//方式1
@Test
public void test6() {
//6. 查询所有记录,将其封装为tab_category对象的List集合。
String sql = "select * from tab_category";
List<Category> categoryList = jt.query(sql, new RowMapper<Category>() {
@Override
public Category mapRow(ResultSet resultSet, int i) throws SQLException {
Category c = new Category();
//resultSet结果集
int cid = resultSet.getInt("cid");
String cname = resultSet.getString("cname");
//设置结果
c.setCid(cid);
c.setCname(cname);
return c;
}
});
//遍历集合
for (Category c : categoryList) {
System.out.println(c);
}
}
//方式2
@Test
public void test6_1() {
//6. 查询所有记录,将其封装为tab_category对象的List集合。
String sql = "select * from tab_category";
List<Category> categoryList = jt.query(sql, new BeanPropertyRowMapper<Category>(Category.class));
for (Category category : categoryList) {
System.out.println(category);
}
}
7. 查询总记录数。
JdbcTemplate jt = new JdbcTemplate(JDBCUtils.getDataSource());
@Test
public void test7() {
//7. 查询总记录数。
String sql = "select count(*) from tab_category";
Integer count = jt.queryForObject(sql, Integer.class);
System.out.println(count);
}
8. 将查询记录,封装到Category对象中。
JdbcTemplate jt = new JdbcTemplate(JDBCUtils.getDataSource());
@Test
public void test8() {
//8.将查询记录,封装到Category对象中。
String sql = "select * from tab_category where cid = ?";
Category category = jt.queryForObject(sql, new BeanPropertyRowMapper<Category>(Category.class), 1);
System.out.println(category);
}
DBUtils
DBUtils其实和JDBCTemplate是一样的,简单封装了JDBC操作,DBUtils是后来加上的,其实有的人也会用到该工具类。就简单写下。
1.ResultSetHandler
导入依赖commons-dbutils-1.7.jar包
我们知道在执行select语句之后得到的是ResultSet,然后我们还需要对ResultSet进行转换,得到最终我们想要的数据。你可以希望把ResultSet的数据放到一个List中,也可能想把数据放到一个Map中,或是一个Bean中。
DBUtils提供了一个接口ResultSetHandler,它就是用来ResultSet转换成目标类型的工具。你可以自己去实现这个接口,把ResultSet转换成你想要的类型。
DBUtils提供了很多个ResultSetHandler接口的实现,这些实现已经基本够用了,我们通常不用自己去实现ResultSet接口了。
- MapHandler:单行处理器!把结果集转换成Map<String,Object>,其中列名为键;
- MapListHandler:多行处理器!把结果集转换成List<Map<String,Object>>;
- BeanHandler:单行处理器!把结果集转换成Bean,该处理器需要Class参数,即Bean的类型;
- BeanListHandler:多行处理器!把结果集转换成List;
- ColumnListHandler:多行单列处理器!把结果集转换成List,使用ColumnListHandler时需要指定某一列的名称或编号,例如:new ColumListHandler(“name”)表示把name列的数据放到List中;
- ScalarHandler:单行单列处理器!把结果集转换成Object。一般用于聚集查询,例如select count(*) from tab_student;
1.Map处理器
2.Bean处理器
3.Column处理器
4.Scalar处理器
2.QueryRunner之查询
QueryRunner的查询方法是:
public T query(String sql, ResultSetHandler rh, Object… params)
public T query(Connection con, String sql, ResultSetHandler rh, Object… params)
query()方法会通过sql语句和params查询出ResultSet,然后通过rh把ResultSet转换成对应的类型再返回。
代码演示
@Test
public void fun1() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student where number=?";
//把一行记录转换成一个Map,其中键为列名称,值为列值。
Map<String,Object> map = qr.query(sql, new MapHandler(), "S_2000");
System.out.println(map);
}
@Test
public void fun2() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
//把转换集转换成List<Map>,其中每个Map对应一行记录。
List<Map<String,Object>> list = qr.query(sql, new MapListHandler());
for(Map<String,Object> map : list) {
System.out.println(map);
}
}
@Test
public void fun3() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student where number=?";
//把结果集转换成一个Bean对象,在使用BeanHandler时需要指定Class,即Bean的类型。
Student stu = qr.query(sql, new BeanHandler<Student>(Student.class), "S_2000");
System.out.println(stu);
}
@Test
public void fun4() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
//把结果集转换成List<Bean>,其中每个Bean对应一行记录。
List<Student> list = qr.query(sql, new BeanListHandler<Student>(Student.class));
for(Student stu : list) {
System.out.println(stu);
}
}
@Test
public void fun5() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
多行单例处理器,即获取name列数据。
List<Object> list = qr.query(sql, new ColumnListHandler("name"));
for(Object s : list) {
System.out.println(s);
}
}
@Test
public void fun6() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select count(*) from tab_student";
//单行单列处理器,一般用于聚合查询,在使用ScalarHandler时可以指定列名,如果不指定,默认为第1列。
Number number = (Number)qr.query(sql, new ScalarHandler());
//对聚合函数的查询结果,有的驱动返回的是Long,有的返回的是BigInteger,所以这里我们把它转换成Number,Number是Long和BigInteger的父类!然后我们再调用Number的intValue()或longValue()方法就OK了。
int cnt = number.intValue();
System.out.println(cnt);
}