JDBC进阶 + 数据源

JDBC进阶

上回我们知道了,通过工厂模式+配置文件动态连接数据库;
这次的主要目的呢,则是将增删改查想办法重构掉;比如查询,到哪里的操作不都是一样的嘛;所以我们自然就想到了将查询操作重构掉;

然后我们思考一下类的组织,有这么一个接口,其中声明了增删改查,我们的类实现这个接口== 我们的类必须实现增删改查,不实现这些,你也叫数据库操作吗?然后我们要将这个类的增删改查的实际操作重构掉;这个类只需要知道该干什么,需要什么齐活;

首先是接口,实现类必须实现增删改查;

1
2
3
4
5
6
7
public interface UserDAO {
public ArrayList<Users> findUsers();
public boolean findUser(Users users);
public int insertUser(Users users);
public int update(Users users);
public int delete(Users users);
}

我们的目标是这样的,只需要参数,和做什么;

1
2
3
4
5
6
7
8
9
10
11
12
public class UserDAOImp1 extends BaseDAO implements UserDAO {
@Override
public ArrayList<Users> findUsers() {
String sql = "select userid Userid ,username Username,userage Userage," +
"gender Gender,birthday Birthday,address Address from users";
return findObjs(sql,Users.class);
}
@Override
public boolean findUser(Users user) {
// TODO Auto-generated method stub
return false;
}

然后就是重点的重构过程了:其实就是增加了一个父类;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class BaseDAO {
public ArrayList findObjs(String sql , Class clazz)
{
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList objs = new ArrayList();
try {
connection = JDBCUtils.getConnection();
ps = connection.prepareStatement(sql);
System.out.println(sql);
rs = ps.executeQuery();
while(rs.next()){
Object obj = mappingObj(rs,clazz); //这里取结果集就厉害了;来什么取什么
objs.add(obj);
}
JDBCUtils.free(rs, ps, connection);
} catch (SQLException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return objs;
}
private Object mappingObj(ResultSet rs,Class clazz) throws SQLException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
//实例化映射对象
Object obj = clazz.newInstance();
//获取映射对象的方法
Method[] methods = clazz.getMethods();
//获取结果集中元数据信息
ResultSetMetaData meta = rs.getMetaData();
// 按字段数目循环结果集中记录,进行对象映射
System.out.println(meta.getColumnCount());
for(int i=1;i<=4;i++){
// 构造当前列的set方法名称
String colname = meta.getColumnLabel(i);
String methodname = "set" + colname;
System.out.println(methodname);
// 循环查找同名方法,并通过反射调用该方法,设置属性
for(Method method:methods){
if(method.getName().equals(methodname)){
System.out.println(methodname+"find");
method.invoke(obj, rs.getObject(i));
break;
}
}
}
return obj;
}
}

上面就是最重要的部分,反射+元数据;我们为什么重构,因为对任意一个表,查询操作是不是都只是将全部数据拿出来。然后放到结果集里;那我们将结果集抽象成对象,必然得set很多啊;比如这样:

1
2
3
4
5
6
7
8
Users users = new Users();
users.setUserid(resultSet.getInt(1));
users.setUsername(resultSet.getString(2));
users.setUserage(resultSet.getInt(3));
users.setGender(resultSet.getInt(4));
users.setBirthday(resultSet.getDate(5));
users.setAddress(resultSet.getString(6));
arrayList.add(users);

现在的情况是什么呢?
你给我你要将结果集放在什么对象里;我从结果集里看看元数据(我的数据长什么样,是不是符合要求);如果符合,如果搭配就初始化到对象中的属性去;而结果集是怎么来的,通过sql语句,sql语句怎么来的?你自己传进去的;
你可以怎么想呢?,你找了一个函数,然后告诉他:去那边找找有没有足球,然后放到这个足球框里;要求和对象都是你给的;这个函数当然也就可以做找篮球,放篮球框里;如果是更改的话,就是:“你找找有没有没气的皮球”,用这个皮球代替一下;

JDBC 数据源

其实这个也很好理解,就是连接是一种资源;在浏览网页的时候,和数据库建立的连接,要可以控制啊;就像A,B之间有5条路,每次一条路只能通过一个人;一旦有人走,就等于可用路线少了;反过来,如果这个人走完了,那么必然总路线有多了一个;
这里写图片描述

数据源要做什么呢? 首先来一个用户,是不是得申请一个连接,所以会有getConnection()方法;数据源有一大堆的连接,那么他们从哪里来的,一定得有创建的啊;还有就是数据源的数据结构,一大堆的数据结构,用的时候,就给一个,结束,就返回一个;所以建造了一个链表;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class MyDataSource implements DataSource {
private LinkedList<Connection> connPool = new LinkedList();
private static String url ;
private static String username ;
private static String password ;
/*
创建Connection;
*/
private Connection createConnection(){
Properties prop = new Properties();
InputStream in = this.getClass().getClassLoader()
.getResourceAsStream("db.properties");
try {
prop.load(in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
try {
Class.forName(prop.getProperty("drivername"));
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Connection realConn = null;
try {
realConn = DriverManager.getConnection(url,username,password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Connection myConn = new MyConnection(realConn, this.connPool);
return myConn;
}
/*
获取一个connection,得到一个连接,
*/
@Override
public Connection getConnection(){
if(this.connPool.size()>0){
Connection conn = (Connection)this.connPool.removeFirst();
// System.out.println(conn.toString());
return conn;
}else{
return createConnection();
}
}
/*
还有好多需要重写的方法;
*/
}

这里使用到了代理,一个连接,怎么也不应该结束连接的时候,还能放到连接池中吧;而很显然,这件事也不适合数据源去做;因为你可以创建连接,但是不能关闭连接,关闭连接是连接自己的行为;所以为了让连接和连接池发生关系,我们使用了代理;(不恰当的比喻)就像中介,既有我们的需求,又有市场的情况,所以就能调和两者;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class MyConnection implements Connection {
private Connection realConn;
private LinkedList<Connection> connPool;
public MyConnection(Connection rConn, LinkedList<Connection> cPool){
this.realConn=rConn;
this.connPool=cPool;
}
@Override
public void clearWarnings() throws SQLException {
// TODO Auto-generated method stub
this.realConn.clearWarnings();
}
@Override
public void close() throws SQLException {
this.connPool.addLast(this);
// System.out.println(realConn.toString());
}
@Override
public void commit() throws SQLException {
this.realConn.commit();
}
/*
还有好多需要重写的方法;
*/
}