连接池
- 概述:开发中,获取以及释放连接,都是非常消耗资源的操作;为了解决该性能问题,通常情况下都是采用连接池技术。有了连接池,就不用自己创建Connection,而是从连接池中获取Connection;而调用close()方法时,就是归还Connection,而不是直接销毁该Connection;等到下次需要再次使用连接时,就再从连接池中获取Connection。
- 规范:Java为数据库连接池提供了公共的接口:javax.sql.DataSource;各个厂商需要让自己的连接池实现这个接口。
自定义连接池
关键技术点
连接池中由于需要频繁地进行添加/移除操作,故而采用LinkedList集合(链表);释放资源时,不执行close()方法,而是将该Connection放入连接池中;等下次需要使用时,再拿出来。
代码
版本01:
public class MyDataSource_V1 implements DataSource {
//创建一个连接池(容器使用LinkedList)
private static LinkedList<Connection> pool = new LinkedList<Connection>();
//初始化连接池(往连接池里放入10个连接)
static {
for (int i = 0; i < 10; ++i) {
//获取连接
Connection conn = JDBCUtils_V3.getConnection();
//把连接放入连接池中
pool.add(conn);
}
}
//这里只需要实现getConnection()方法,其余方法不实现(或者是空实现)
@Override
public Connection getConnection() throws SQLException {
//获取连接之前先判断连接池里有没有连接
if (pool.size() > 0) {
//如果有连接,那就直接获取即可
return pool.removeFirst();
} else {
//如果没有连接,那就往连接池中添加10个连接
for (int i = 0; i < 10; ++i) {
pool.add(JDBCUtils_V3.getConnection());
}
}
return pool.removeFirst();
}
//释放连接(把连接归还到连接池中)
public void backConnection(Connection conn) {
pool.add(conn);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
版本02:
升级:版本2.0相比较于版本1.0,提升的地方在于使用close方法时,V2.0是把连接归还到连接池中,而不是释放资源。
升级实现原理:
- 创建MyConnection类并继承Connection接口;
- 创建成员变量conn和pool;
- 写有参构造函数,把conn和pool作为参数传入构造函数中;
- 因为继承了Connection接口,所以要实现方法,但这里只需要实现close()和prepareStatement(sql)方法,其余方法只需要空实现即可;
这里还需要实现prepareStatement(sql)方法的原因是,放入连接池中的是MyConnection而不是Connection对象,故而当调用conn.prepareStatement(sql)方法时,如果不在MyConnection类里面实现prepareStatement(sql)方法,那么该方法就会空实现(也就是会return null),那么就会报错。
public class MyConnection implements Connection {
//成员变量Connection(连接)
private Connection conn;
//成员变量(连接池,因为增强close方法时,需要把连接归还给连接池)
private LinkedList<Connection> pool;
//有参构造方法(把Connection对象传入,从而进行增强)
public MyConnection(Connection conn, LinkedList<Connection> pool) {
this.conn = conn;
this.pool = pool;
}
@Override
public void close() throws SQLException {
//增强close方法
pool.add(conn);
}
//要重写prepareStatement方法
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return conn.prepareStatement(sql);
// return null;
}
//其余方法都是空实现
}
public class MyDataSource_V2 implements DataSource {
//创建一个连接池(容器使用LinkedList)
private static LinkedList<Connection> pool = new LinkedList<Connection>();
//初始化连接池(往连接池里放入10个连接)
static {
for (int i = 0; i < 10; ++i) {
//获取连接
Connection conn = JDBCUtils_V3.getConnection();
//创建MyConnection对象
MyConnection myConnection = new MyConnection(conn, pool);
//把连接放入连接池中
pool.add(myConnection); //此时放入的是MyConnection对象了
//所以,获取到的也是MyConnection对象了
}
}
//这里只需要重写getConnection()方法,其余方法不重写
@Override
public Connection getConnection() throws SQLException {
//获取连接之前先判断连接池里有没有连接
if (pool.size() > 0) {
//如果有连接,那就直接获取即可
return pool.removeFirst();
} else {
//如果没有连接,那就往连接池中添加10个连接
for (int i = 0; i < 10; ++i) {
pool.add(new MyConnection(JDBCUtils_V3.getConnection(), pool));
}
}
return pool.removeFirst();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
C3P0的使用
- 导包:
- 写配置文件:
配置文件中有两个配置,一个是默认配置(default-config),另一个是有名字的配置(named-config)。这两者的区别在于,当创建ComboPooledDataSource时,如果不传参数,那么就加载默认的配置信息;如果传入参数(就是name=“qishiyi"中的"qishiyi”),那么就根据name来加载有名字的配置信息。 - 加载配置文件:
ComboPooledDataSource ds=new ComboPooledDataSource();
- 获取连接:
Connection ds.getConnection();
DBCP连接池
- 导包:
- 写配置文件:
- 加载配置文件:
- 使用Properties对象加载配置信息:
- 创建DataSource:
DataSource ds=BasicDataSourceFactory.createDataSource(prop);
- 获取Connection:
Connection ds.getConnection();
- 使用Properties对象加载配置信息:
DBCPUtils(自己封装的)
Druid的使用
- 导包:
- 写配置文件:
- 加载配置文件:
- 获取Connection:
DataSource ds = DruidDataSourceFactory.createDataSource(prop);
- 小结:
- DruidUtils:
public class DruidUtils {
private static DataSource ds;
static {
InputStream in = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties prop = new Properties();
try {
prop.load(in);
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
public static void close(Connection conn, Statement statement) throws SQLException {
if (statement != null)
statement.close();
if (conn != null)
conn.close();
}
}
JDBCTemplate
- 练习
- 代码:
public class JDBCTemplateDemo {
/**
* 1. 修改1号数据的 salary 为 10000
* 2. 添加一条记录
* 3. 删除刚才添加的记录
* 4. 查询id为1的记录,将其封装为Map集合
* 5. 查询所有记录,将其封装为List
* 6. 查询所有记录,将其封装为Emp对象的List集合
* 7. 查询总记录数
*/
private JdbcTemplate jdbcTemplate = new JdbcTemplate(DruidUtils.getDataSource());
//1. 修改92号数据的内容为“修改记录”
@Test
public void test01() {
String sql = "update weibo set wcontent=? where wid=?";
int update = jdbcTemplate.update(sql, "修改记录", 92);
System.out.println(update);
}
//2. 添加一条记录
@Test
public void test02() {
String sql = "insert into weibo values(null,?,?,?,?,?)";
int update = jdbcTemplate.update(sql, "添加一条记录", "2021-07-28 09:21:23", "0", "0", "PC");
System.out.println(update);
}
//3. 删除刚才添加的记录
@Test
public void test03() {
String sql = "delete from weibo where wid=?";
int update = jdbcTemplate.update(sql, 93);
System.out.println(update);
}
//4. 查询id为92的记录,将其封装为Map集合
@Test
public void test04() {
String sql = "select * from weibo where wid=?";
//封装成Map时只能封装一条数据
Map<String, Object> map = jdbcTemplate.queryForMap(sql, 92);
System.out.println(map);
}
//5. 查询所有记录,将其封装为List
@Test
public void test05() {
String sql = "select * from weibo";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
for (Map<String, Object> m : maps) {
System.out.println(m);
}
}
//6. 查询所有记录,将其封装为Emp对象的List集合
@Test
public void test06_01() {
String sql = "select * from weibo";
List<MyWeibo> list = jdbcTemplate.query(sql, new RowMapper<MyWeibo>() {
@Override
public MyWeibo mapRow(ResultSet rs, int i) throws SQLException {
//封装实体(MyWeiBo)
MyWeibo myWeibo = new MyWeibo();
myWeibo.setWcontent(rs.getString("wcontent"));
//封装其余剩下的
return myWeibo;
}
});
for (MyWeibo myWeibo : list)
System.out.println(myWeibo);
}
@Test
public void test06_02() {
String sql = "select * from weibo";
List<MyWeibo> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<MyWeibo>(MyWeibo.class));
for (MyWeibo myWeibo : list)
System.out.println(myWeibo);
}
//7. 查询总记录数
@Test
public void test07() {
String sql = "select count(wid) from weibo";
Long count = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println(count);
}
}
JavaBean
JavaBean就是一个类,它在开发中常用于封装数据,其要求如下:
实现java.io.Serializable接口;提供成员变量(私有字段)private 数据类型 变量名;提供无参构造函数;提供get/set方法。
DBUtils
- 介绍:
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,创建连接、结果集封装、释放资源,同时也不会影响程序的性能。因此dbutils成为很多不喜欢hibernate的公司的首选。DBUtils是Java编程中的数据库操作工具,小巧实用。DBUtils封装了对jdbc的操作,可以减少代码量 - DBUtils的三个核心功能:
- QueryRunner
- 作用:提供数据库连接池,DBUtils底层自动维护连接Connection(连接的获得、释放等)。
- 创建:
new QueryRunner(DataSource ds);
数据库配置信息是根据传入的连接池的配置信息而来的。 - 方法:
int update(String sql,Object ... params)
:执行增删改,具体是根据传入的sql语句来执行的;返回影响的行数;pamams是参数,一般是创建一个Object类型的数组params,把这个数组传入。query(String sql,ResultSetHandler handler,Object ... params)
- ResultSetHandler
- BeanHandler:
new BeanHandler<T>(T.class)
把查询到的第一条数据封装到Bean对象中。 - BeanListHandler:
new BeanListHandler<T>(T.class)
把查询的数据集(可以有很多条数据)封装到Bean对象中。 - ScalarHandler:
new Scalar()
它用于单数据,例如select count(*) from table_name,返回Long类型数据。 - ColumnListHandler:
new ColumnListHandler(String columnName)
把查询到的数据(可以有很多条数据)的某一个字段封装到List集合中,具体是哪一个字段要根据传入的参数columnName来决定。是没有ColumnHandler的;只有ColumnListHandler
- MapHandler:
new MapHandler()
把查询到的数据(如果查询到很多条数据就只获取第一条数据)以键值对的形式封装到Map集合中,键是字段名,值是该字段的值。 - MapListHandler:
new MapListHandler()
把查询到的数据(可以是多条数据)以键值对的形式封装到一个类型为Map<String,Object>的List集合中。也就是一个Map里面有好几个键值对,不过这一个Map只是List集合中的一个“元素”,还有其他很多的Map。
- BeanHandler:
- QueryRunner