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) { 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++){ 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) { e.printStackTrace(); } url = prop.getProperty("url"); username = prop.getProperty("username"); password = prop.getProperty("password"); try { Class.forName(prop.getProperty("drivername")); } catch (ClassNotFoundException e) { e.printStackTrace(); } Connection realConn = null; try { realConn = DriverManager.getConnection(url,username,password); } catch (SQLException e) { 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(); 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 { this.realConn.clearWarnings(); } @Override public void close() throws SQLException { this.connPool.addLast(this); } @Override public void commit() throws SQLException { this.realConn.commit(); } 还有好多需要重写的方法; */ }
|