传智播客李勇Jdbc视频笔记(34-42)

34、编写一个基本的连接池来实现连接的复用
大家都知道Arraylist的底层使用数组实现的,而LinkedList使用链表实现的,所以对于Arraylist读取速度比较快而对于LinkedList修改和添加
比较快,所以我们这个连接池因为要频繁的操作集合所以用LinkedList来实现。
public class MyDataSource {
private static String url = "jdbc:mysql://localhost:3306/jdbc?generateSimpleParameterMetadata=true";
private static String user = "root";
private static String password = "admin";

LinkedList<Connection> connectionsPool = new LinkedList<Connection>();

//向我们的LinkedList集合中加入10个链接作为我们的连接池
public MyDataSource() {
for(int i=0; i<10; i++) {
try {
//将Connection放到链表的最后面
connectionsPool.addLast(this.createConnection());
} catch (SQLException e) {
throw new ExceptionInInitializerError(e.getMessage());
}
}
}
//创建10个链接
public Connection createConnection() throws SQLException{
return DriverManager.getConnection(url, user, password);
}

//得到一个链接(先进先出算法)
public Connection getConnection() {
return connectionsPool.removeFirst();
}

//关闭一个链接,这个关闭不是真正意义上的关闭,而是又把他放回到连接池中,实现了Connection的复用
public void free(Connection conn) {
connectionsPool.addLast(conn);
}
}


35、对基本连接池进行一些工程细节上的优化
在上面实现的连接池中我们只是默认创建了5个连接,但是如果这个时候有5个线程同时都来拿连接,那连接池里就没有连接
在有线程过来拿的时候就会报错了,现在我们进行一些优化(重复的代码就不写了,只写改动的)
//规定默认创建的连接数
private static int initCount = 5;
//规定最大可以创建的连接数
private static int maxCount = 10;
//统计当前共创建了多少个连接
private int currentCount = 0;

LinkedList<Connection> connectionsPool = new LinkedList<Connection>();

public MyDataSource() {
for(int i=0; i<initCount; i++) {
try {
connectionsPool.addLast(this.createConnection());
//每创建一个链接 currentCount ++
this.currentCount ++;
} catch (SQLException e) {
throw new ExceptionInInitializerError(e.getMessage());
}
}
}

public Connection createConnection() throws SQLException{
return DriverManager.getConnection(url, user, password);
}

public Connection getConnection() throws SQLException {
//因为Connection不是线程安全的,所以我必须保证每个线程拿到的链接不是一个,所以要进行同步:当两个线程同时来拿的时候
//另外一个线程必须等待
synchronized (connectionsPool) {
if(connectionsPool.size() > 0)
return connectionsPool.removeFirst();
//如果当前创建的链接数没有到最大值,那就继续创建链接
if(this.currentCount < maxCount) {
this.currentCount ++;
return this.createConnection();
}
//抛出异常
throw new SQLException("当前已经没有可用连接了");
}
}


36、通过代理模式来保持用户关闭连接的习惯.
在上面的示例中我们在关闭链接的时候,调用的是free方法来把这个连接又放回到了池中,但是按照开发人员的使用习惯应该是调用colse()方法
来关闭一个链接,但是如果调用close方法关闭,那这个连接就真的关闭了,也就是说我们这个方法设计的不符合开发人员的使用习惯下面我用代理的
方法来解决这个问题:
定义一个类实现Connection接口,Connectio接口中有很多的方法,这些方法我们都无法自己完成,我们交给通过构造方法传递进来的真正的Connection
的对象来完成,我们只是修改它的close方法,在用户得到链接的时候我们返回给用户这个类的对象,那么当用户调用close方法关闭链接的时候,我们就可以在
这个close方法中将用户要关闭的那个链接再次的放到连接池中,这样链接就不会真正的关闭了。
public class MyConnection implements Connection {

MyDataSource2 dataSource;
Connection realConn;
int maxUseCount = 5;
int currentUseCount = 0;

MyConnection(MyDataSource2 myDataSource2, Connection conn) {
this.dataSource = myDataSource2;
this.realConn = conn;
}

public void clearWarnings() throws SQLException {
realConn.clearWarnings();
}

public void close() throws SQLException {
this.currentUseCount++;
//规定同一个链接只能使用maxUseCount次超过这个次数,就把真正的链接关闭,这个时候在拿连接,拿到的就是
新创建得一个新的链接对象了。
if (this.currentUseCount < this.maxUseCount) {
this.dataSource.connectionsPool.addLast(this);
}
else {
this.realConn.close();
this.dataSource.currentCount--;
}
}

public void commit() throws SQLException {
this.realConn.commit();
}


在类MyDataSource中我们别的都不改变,只改变他的一个方法(MyDataSource的全部代码查看34)
当用户想拿到链接的时候返回给他conn类的一个代理对象myConnection对象就可
public Connection createConnection() throws SQLException{
Connection conn = DriverManager.getConnection(url, user, password);
MyConnection myConnection = new MyConnection(this, conn);
return myConnection;
}


37、Java的动态代理及使用该技术完善连接代理
在上面的示例中,我们为了产生一个代理对象实现了Connection接口的所有的方法,但是我们只需要修改它的close方法,别的方法
我们都需要交给真正的Connection对象去处理,比较麻烦我们用动态代理来实现它

public class MyConnectionHandler implements InvocationHandler {

private Connection conn;
private MyDataSource2 dataSource;
private int maxUseCount = 5;
private int currentUseCount = 0;
private Connection proxyConn;

MyConnectionHandler(MyDataSource2 dataSource) {
this.dataSource = dataSource;
}

Connection bind(Connection realConnection) {
this.conn = realConnection;
//生成Connection的代理对象
proxyConn = (Connection)Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[]{Connection.class}, this);
//System.out.println(proxyConn + "代理");
return proxyConn;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//调用方法的时候如果是close方法就执行我们的逻辑,对于其他的所有的方法,全部交给真实Connection对象本身自己去处理
if("close".equals(method.getName())) {
this.currentUseCount ++;
if(this.currentUseCount < this.maxUseCount) {
this.dataSource.free(proxyConn);
}
else {
this.conn.close();
this.dataSource.currentCount --;
}
}
return method.invoke(conn, args);
}
}


当写完上面的代理类后,我们还是需要修改MyDataSource类的createConnection()方法来调用我们的代理类,将它需要的参数
传递给他并把生成的代理类返回:
public Connection createConnection() throws SQLException{
Connection conn = DriverManager.getConnection(url, user, password);
MyConnectionHandler proxyConn = new MyConnectionHandler(this);
return proxyConn.bind(conn);
}


38、标准DataSource接口及数据源的总结介绍
理解数据源的优势与特点:
DataSource用来取代DriverManager来获取Connection;
通过DataSource获得Connection速度很快;
通过DataSource获得的Connection都是已经被包裹过的(不是驱动原来的连接),他的close方法已经被修改。
一般DataSource内部会用一个连接池来缓存Connection,这样可以大幅度提高数据库的访问速度;
连接池可以理解成一个能够存放Connection的Collection;
我们的程序只和DataSource打交道,不会直接访问连接池;

39、如何使用开源项目DBCP(实际项目中常用):主要分为三个步骤
(1)使用DBCP必须用的三个包:commons-dbcp-1.2.1.jar, commons-pool-1.2.jar, commons-collections-3.1.jar。
(2)添加dbcp的配置文件
(3)Java API: BasicDataSourceFactory.createDataSource(properties);

在我们上面的示例中我们的MyDataSource可以实现Datasource接口,实现里面的getConnection()方法,我们想用dbcp的时候,把
数据源的实现换成dbcp就行,因为这个组件也实现了DataSource接口,所以这就是面向接口编程的好处,可以换成不同的实现 ,不用
改变我们其他的代码

40、将DAO中的修改方法提取到抽象父类中:
当你在写程序的时候,如果你发现你的代码总是有重复的地方那么就有必要封装一下了。把一段代码中的变化的部分
抽取到父类里,把要用的参数传递过去,然后再实现类里面直接super调用父类的方法就可以了。把参数传递过去就行了
可以向外提过多个方法的重载 但是真正的代码实现只有一份。

41、使用模板方法设计模式处理DAO中的查询方法
在这个示例中需要用到多态的知识一般我们说满足多态要有三个条件:
(1)要有继承
(2)要有方法的重写
(3)要有父类的引用指向子类对象
但是有一种情况是特殊的:如果子类调用父类的方法(包括构造方法)而在你调用的这个父类的方法里你又调用了父类中的其他的方法,而你调用的这个
方法又被子类重写了,那么这就是多态

简单的程序示例:
抽象类
public abstract class AbstractDao {
abstract public String printName(String name) ;
public String test2() {
this.printName("aaa");
return "你好";
}
}

//调用抽象类的方法
public class AbstractDaoImpl1 extends AbstractDao {

public void getName() {
super.test2();

}

@Override
public String printName(String name) {
return null;
}
}


我们用一个抽象类把重复的方法封装到里面,那么多个类都实现了父类的方法,在调用的时候我们怎么知道调用的是哪个
实现方法呢,这个时候Java的多态特性就发挥作用了:你在实现类中调用父类的方法,而你重写了你要调用的这个方法,那么
你调用的就是你自己实现的这个方法。
把相同的代码抽取出来封装成为一个方法,把容易变动的地方作为方法的参数传递进去 ,这样我们的Dao层会省去很多的重复的代码
而这个封装的方法就可以称之为“模板方法”

42、使用策略模式对模板方法设计模式进行改进
sql1 = "select name from user where id = ?";
sql2 = "select id, name, age from user where id = ?";
这两个sql查询的对象不一样,一个是只需要返回一个name属性的值就可以,而另外一个sql需要返回一个User
对象,这样的话我们上面的模板方法就有不好用了,虽然可以查出来但是性能上有损失 我只要查询一个username
你可能会把整个User对象都给我查询出来。我们可以针对每个不同的sql语句查询的内容的不同把模板方法也分解成
多个不一样的能满足相应sql查询语句的方法,这就叫做策略模式,就是针对每一种情况都有不同的方法,来解决
//行映射器
public interface RowMapper {
public Object mapRow(ResultSet rs) throws SQLException;
}
//对于方法的封装
public class MyDaoTemplate {
public Object find(String sql, Object[] args, RowMapper rowMapper) {
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();
Object obj = null;
if (rs.next()) {
obj = rowMapper.mapRow(rs);
}
return obj;
} catch (SQLException e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
}

//对于下面的不同的sql,在我们调用find方法的时候new RowMapper然后针对不同的sql有不同的实现就可以了
//JdbcTemplete也为我们封装了这个方法。
MyDaoTemplate template = new MyDaoTemplate();
public String findUserName(int id) {
String sql = "select name from user where id=?";
Object[] args = new Object[] { id };
Object name = this.template.find(sql, args, new RowMapper() {

public Object mapRow(ResultSet rs) throws SQLException {
return rs.getString("name");
}
});
return (String) name;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值