转眼自己已经学习java一年多了,从最初的使用驱动加载连接数据库,到后来的使用阿里巴巴开发的连接池操作数据库再到后来的SpringJDBC操作数据库以及mybatis操作数据库。操作数据库的方法在不断更新升级,其目的是让程序员从sql语句和sql语句的安全性这些工作中解放出来,将精力更多地放在业务逻辑和工程的架构搭建上。
那么,今天我就将我最初学习java时的连接数据库的方式以及我的一些理解进行思考。
我们知道,操作数据库对我们来说是十分重要的,数据库的数据可以长久地保存在磁盘上。在我们需要的时候进行调用存取。那么我先简介介绍最初的jdbc操作,注意这是最基础的,也是最重要的。因为无论是什么样的操作数据库的方法都是以它为模板进行不断的封装。
首先,我将和数据库有关的连接查询操作封装到了一个DBUtil类中。代码非常简单,只不过我将其进行了分离和封装,从业务逻辑中进行了抽出。
public class DBUtil { private final static String user = "root";; private final static String url = "jdbc:mysql://localhost:3306/mydb"; private final static String password = "l19980207"; private final static String driverName = "com.mysql.jdbc.Driver"; /** * 获取连接 * @return */ public static Connection getConnection(){ try { Class.forName(driverName); } catch (ClassNotFoundException e1) { System.out.println("驱动加载异常!!!!"); e1.printStackTrace(); } Connection conn =null; try { conn = DriverManager.getConnection(url, user, password); } catch (SQLException e) { System.out.println("数据库加载异常"); e.printStackTrace(); } return conn; } /** * 使用普通方式声明连接一次数据库 * @param conn * @return */ public static Statement getStatement(Connection conn){ try { return conn.createStatement(); } catch (SQLException e) { System.out.println("Statement加载异常"); e.printStackTrace(); } return null; } /** * 使用预编译的方式连接一次数据库 * @param conn * @param sql * @return */ public static PreparedStatement getPreparedStatment(Connection conn,String sql){ try { return conn.prepareStatement(sql); } catch (SQLException e) { System.out.println("PreparedStatement加载异常"); e.printStackTrace(); } return null; } /** * 返回一个结果集 * @param state * @param sql * @return */ public static ResultSet getResultSet(Statement state,String sql){ try { return state.executeQuery(sql); } catch (SQLException e) { System.out.println("结果集返回异常"); e.printStackTrace(); } return null; } /** * 关闭与数据库的连接 * @param conn */ public static void close(Connection conn){ try { conn.close(); } catch (SQLException e) { System.out.println("Connection连接关闭失败"); e.printStackTrace(); } } /** * 关闭与statment的连接. * @param state */ public static void close(Statement state){ try { state.close(); } catch (SQLException e) { System.out.println("Statement关闭失败"); e.printStackTrace(); } } /** * 关闭结果集 * @param rs */ public static void close(ResultSet rs){ try { rs.close(); } catch (SQLException e) { System.out.println("ResultSet关闭失败"); e.printStackTrace(); } } /** * 关闭PreparedStatement * @param ps */ public static void close(PreparedStatement ps){ try { ps.close(); } catch (SQLException e) { System.out.println("PreparedStatement关闭失败"); e.printStackTrace(); } } }
这是我的DBUtil类中的代码,包括数据的连接关闭,以及Statement(声明)和PreparedStatement(预编译的操作,建议使用这一种防止sql注入).
然后我在我的dao层写了一个很简单的select查询操作.
public static List<Manager> getAllManagerInfo() throws SQLException{ List<Manager> list = new LinkedList<Manager>(); Connection conn = DBUtil.getConnection(); Statement state = DBUtil.getStatement(conn); String sql = "select * from manager_info"; ResultSet rs = DBUtil.getResultSet(state, sql); while(rs.next()){ Manager manager = new Manager(); manager.setManagerName(rs.getString("manager_name")); manager.setIdentity(rs.getInt("identity")); manager.setGender(rs.getString("gender")); manager.setManagerId(rs.getString("manager_id")); manager.setAge(rs.getInt("age")); manager.setPassword(rs.getString("password")); list.add(manager); } //断开连接 DBUtil.close(rs); DBUtil.close(state); DBUtil.close(conn); return list; }
这个DBUtil已经很久没有使用了,所以我进行了一个简单的复习。
接下来,重点介绍第二种,关于阿里巴巴的连接池的方法操作数据库。具体的连接池的原理大家可以上网具体了解。
连接池的概念 用池来管理Connection,这可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。
首先我们需要连接连接池。
public class DBUtil { private static DruidDataSource dds =null; static{ dds = new DruidDataSource(); dds.setDriverClassName("com.mysql.jdbc.Driver"); dds.setUrl("jdbc:mysql://localhost:3306/mydb"); dds.setUsername("root"); dds.setPassword("l19980207"); } public static DruidDataSource getDruidDataSource(){ return dds; } public static QueryRunner getQueryRunner(){ return new QueryRunner(dds); } /** * 连接池关闭 */ public static void close(){ dds.close(); } }
当我们使用QueryRunner进行查询操作时,需要实现ResultSetHandler<T>中的handle方法.
public List<Manager> handle(ResultSet rs) throws SQLException { List<Manager> list = new LinkedList<Manager>(); while(rs.next()){ Manager manager = new Manager(); manager.setManagerName(rs.getString("manager_name")); manager.setIdentity(rs.getInt("identity")); manager.setGender(rs.getString("gender")); manager.setManagerId(rs.getString("manager_id")); manager.setAge(rs.getInt("age")); manager.setPassword(rs.getString("password")); list.add(manager); } return list; }
具体的操作时通过DBUtil.getQueryRunner().query(sql, new ManagerInfoResultSetHandler())进行获取list。
于是我打开了QueryRunner这个类中的query方法。
private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException { if (conn == null) { throw new SQLException("Null connection"); } if (sql == null) { if (closeConn) { close(conn); } throw new SQLException("Null SQL statement"); } if (rsh == null) { if (closeConn) { close(conn); } throw new SQLException("Null ResultSetHandler"); } PreparedStatement stmt = null; ResultSet rs = null; T result = null; try { stmt = this.prepareStatement(conn, sql); this.fillStatement(stmt, params); rs = this.wrap(stmt.executeQuery()); result = rsh.handle(rs); } catch (SQLException e) { this.rethrow(e, sql, params); } finally { try { close(rs); } finally { close(stmt); if (closeConn) { close(conn); } } } return result; }
从中我们可以看到,这个query()方法使用的PreparedStatement进行访问数据库,这样可以防止sql注入。
result = rsh.handle(rs);
从这一行代码中,我们可以了解,ResultSetHandler将我们查询到的结果集rs将其赋值给泛型T,也就是我们需要返回的引用类型。
这个赋值的方法应该是封装调用了我们的Object.setValue(rs.getValue("columnName"));
在我看来,我们需要多去查看源码,而不是简单地记忆代码,这样才可以使我们的代码更富有灵活性。