JDBC存在的问题
- 代码的冗余:在对数据库进行增删改查时,每个操作的JDBC流程和SQL执行代码的流程都一样,造成代码的冗余,所以咱们能够把冗余的局部封装起来,封装之后,咱们就不必再去写JDBC流程,只须要些SQL语句---这一部分的封装下一篇文章形容
- 性能问题:每一次对数据库的操作都要建设连贯,操作完之后又要开释连贯,如果数据库拜访次数不多时性能还能够,但当咱们的程序有很多人应用时,对数据库的操作次数也会越来越多,每一次操作都要建设和敞开连贯,势必会升高性能,本篇文章次要解决这部分的问题
如何解决性能问题
咱们先用一个例子来形容一下这个JDBC性能问题。
- 比如说,当两个中央原本相互不通,别离是A和B两个中央。这时咱们就须要在A和B之间修一条路能让他们连通,有人在这条路上通行的时候就处于连贯状态,然而当这条路可能有段时间没人走了咱们就把它炸掉了。然而每当有人须要从A去到B时,又须要再修一条路,而每一次修一条路的工夫都须要十分久。所以咱们不能一不必这条路就把它炸掉,这样十分浪费时间和资金。那咱们该怎么解决呢?咱们在A和B两地之间多修几条路,在每条路的路口设置关卡,当有人在其中一条路上通行时,那么这个关卡就不容许其他人在这条路上通行,当路上没人通行时那么关卡容许其他人通行。(可能这举的例子和理论生存不是特地失当,但也心愿大家可能了解,切实想不到一个更为贴切的例子)
- 解决JDBC性能问题也是这个情理,一开始咱们就创立几个连贯通道,用完之后不敞开,而是用一个标记位来示意以后通道是否可用,建设的这几条通道放在对立的一个中央进行治理,看起来就像个池子一样,所以就叫JDBC连接池
封装JDBC的步骤
- 1、为了更好的治理我的项目,咱们把连贯数据库用到的驱动类、url地址、用户名和明码都放到一个properties文件里,之后改起来也不便,一处改变全副都跟着改变。须要一个类来帮咱们读取配置文件的信息
- 配置信息如下(Configuration.properties)
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/bank?serverTimezone=CST user=root password=123456 minConnectCount=5 waitTime=5
- ConfigReader类
package util; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class ConfigReader { private static Properties properties; //用Map汇合寄存properties文件下的配置信息 private static Map<String, String> configMap; //配置文件只加载一次 static { properties = new Properties(); configMap = new HashMap<>(); InputStream inputStream = null; try { inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("configuration.properties"); properties.load(inputStream); Enumeration en = properties.propertyNames(); while (en.hasMoreElements()){ String key = (String) en.nextElement(); String value = properties.getProperty(key); configMap.put(key, value); } } catch (IOException e) { e.printStackTrace(); }finally { if (inputStream != null){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } //获取配置文件外面的value值 public static String getPropertyValue(String key){ return configMap.get(key); } }
- 2、在连贯JDBC的时候咱们用DriverManager.getConnection()办法获取连贯,返回了Connection对象,胜利获取后也表明和数据库连贯胜利,当咱们不须要用的时候调用close()办法敞开连贯。为了实现用完之后不敞开连贯,咱们须要本人封装一个类,这个类下有一个Connection属性,还有一个标记位,用来判断该连贯可用还是不可用。为了可能让本人封装的类和Connection类的应用起来是一样的,咱们本人创立一个AdapterConnection抽象类,这个类实现了Connection接口,咱们本人封装的Connection类继承了这个抽象类,并重写了其中的三个办法,别离用来创立状态参数以及敞开连贯。(这部分的设计思维体现出缺省适配器模式)
- AdapterConnection类
package pool; import java.sql.*; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; public abstract class AdapterConnection implements Connection { //这里将这三个写为形象办法,是为了当子类继承这个抽象类的时候提醒子类必须重写这个三个办法 @Override public abstract Statement createStatement() throws SQLException; @Override public abstract PreparedStatement prepareStatement(String sql) throws SQLException ; @Override public abstract void close() throws SQLException; @Override public CallableStatement prepareCall(String sql) throws SQLException { return null; } @Override public String nativeSQL(String sql) throws SQLException { return null; } @Override public void setAutoCommit(boolean autoCommit) throws SQLException { } @Override public boolean getAutoCommit() throws SQLException { return false; } @Override public void commit() throws SQLException { } @Override public void rollback() throws SQLException { } @Override public boolean isClosed() throws SQLException { return false; } @Override public DatabaseMetaData getMetaData() throws SQLException { return null; } @Override public void setReadOnly(boolean readOnly) throws SQLException { } @Override public boolean isReadOnly() throws SQLException { return false; } @Override public void setCatalog(String catalog) throws SQLException { } @Override public String getCatalog() throws SQLException { return null; } @Override public void setTransactionIsolation(int level) throws SQLException { } @Override public int getTransactionIsolation() throws SQLException { return 0; } @Override public SQLWarning getWarnings() throws SQLException { return null; } @Override public void clearWarnings() throws SQLException { } @Override public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { return null; } @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return null; } @Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return null; } @Override public Map<String, Class<?>> getTypeMap() throws SQLException { return null; } @Override public void setTypeMap(Map<String, Class<?>> map) throws SQLException { } @Override public void setHoldability(int holdability) throws SQLException { } @Override public int getHoldability() throws SQLException { return 0; } @Override public Savepoint setSavepoint() throws SQLException { return null; } @Override public Savepoint setSavepoint(String name) throws SQLException { return null; } @Override public void rollback(Savepoint savepoint) throws SQLException { } @Override public void releaseSavepoint(Savepoint savepoint) throws SQLException { } @Override public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return null; } @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return null; } @Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return null; } @Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { return null; } @Override public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { return null; } @Override public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { return null; } @Override public Clob createClob() throws SQLException { return null; } @Override public Blob createBlob() throws SQLException { return null; } @Override public NClob createNClob() throws SQLException { return null; } @Override public SQLXML createSQLXML() throws SQLException { return null; } @Override public boolean isValid(int timeout) throws SQLException { return false; } @Override public void setClientInfo(String name, String value) throws SQLClientInfoException { } @Override public void setClientInfo(Properties properties) throws SQLClientInfoException { } @Override public String getClientInfo(String name) throws SQLException { return null; } @Override public Properties getClientInfo() throws SQLException { return null; } @Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException { return null; } @Override public Struct createStruct(String typeName, Object[] attributes) throws SQLException { return null; } @Override public void setSchema(String schema) throws SQLException { } @Override public String getSchema() throws SQLException { return null; } @Override public void abort(Executor executor) throws SQLException { } @Override public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { } @Override public int getNetworkTimeout() throws SQLException { return 0; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } }
- MyConnection类
package pool; import util.ConfigReader; import java.sql.*; public class MyConnection extends AdapterConnection{ private Connection conn; //标记位,true示意被占用,false示意该通道可用 private boolean used = false; private static String driver; private static String url; private static String user; private static String password ; //动态块,让加载类的步骤只执行一次,并初始化四个属性 static { try { driver = ConfigReader.getPropertyValue("driver"); url = ConfigReader.getPropertyValue("url"); user = ConfigReader.getPropertyValue("user"); password = ConfigReader.getPropertyValue("password"); Class.forName(driver); }catch (ClassNotFoundException e){ e.printStackTrace(); } } //用来初始化连贯通道,每一次被创立时都进行初始化 { try { conn = DriverManager.getConnection(url, user, password); }catch (SQLException e){ e.printStackTrace(); } } //获取连贯通道 public Connection getConn() { return conn; } //判断该连贯通道是否可用 public boolean isUsed() { return used; } //设置标记位 public void setUsed(boolean used) { this.used = used; } @Override public Statement createStatement() throws SQLException { return this.conn.createStatement(); } //重写了继承自AdapterConnection类的办法 //该办法是为了获取状态参数,实质上还是调用了PreparedStatement @Override public PreparedStatement prepareStatement(String sql) throws SQLException { PreparedStatement pstate = this.conn.prepareStatement(sql); return pstate; } //重写了继承自AdapterConnection类的办法 //该办法的目标是为了将连贯通道设置为可用,看起来就像敞开流一样 @Override public void close() throws SQLException { this.used = false; } }
- 3、咱们须要一个连接池对象来帮咱们治理连贯对象,咱们须要把连贯对象放到一个List汇合中,当咱们须要的时候就从汇合里取。连贯对象的个数咱们也搁置在properties文件里,便于管理。因为连贯对象无限,当有多个用户同时拜访时,可能有的用户拿不到连贯对象,这里用了一个期待机制,当用户拿不到连贯对象时,就让它等一会,如果超出等待时间还拿不到,就抛出一个异样告诉用户。
- ConnectionPool类
package pool; import util.ConfigReader; import javax.swing.*; import java.io.InputStream; import java.sql.Connection; import java.util.ArrayList; import java.util.List; public class ConnectionPool { //将ConnectionPool设计成单例模式 private ConnectionPool(){} private static volatile ConnectionPool connectionPool; public static ConnectionPool getInstance(){ if (connectionPool == null){ synchronized (ConnectionPool.class){ if (connectionPool == null){ connectionPool = new ConnectionPool(); } } } return connectionPool; } //获取最小连贯个数以及等待时间 private int minConnectCount = Integer.parseInt(ConfigReader.getPropertyValue("minConnectCount")); private int waitTime = Integer.parseInt(ConfigReader.getPropertyValue("waitTime")); //属性---List汇合,用来存储连贯对象 private List<Connection> pool = new ArrayList<>(); //往pool汇合外面寄存连贯对象 { for (int i = 1; i <= minConnectCount; i ++){ pool.add(new MyConnection()); } } //办法,获取连贯对象 private Connection getMC(){ Connection result = null; //遍历连接池中的对象 for (Connection conn : pool){ MyConnection mc = (MyConnection) conn; if (!mc.isUsed()){//示意连贯是可应用的 synchronized (ConnectionPool.class){ if (!mc.isUsed()){ mc.setUsed(true); result = mc; } } break; } } return result; } //该办法是为了获取连贯对象,并减少了期待机制 public Connection getConnection(){ Connection result = this.getMC(); int count = 0;//记录循环的次数 while (result == null && count < waitTime*10){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } result = this.getMC(); count++; } if (result == null){ throw new SystemBusyException("以后零碎忙碌,请稍后再试"); } return result; } }
自定义异样类SystemBusyException package pool; public class SystemBusyException extends RuntimeException { public SystemBusyException(){} public SystemBusyException(String msg){ super(msg); } }
这样整个JDBC连接池就封装好了,整体的性能也比较简单,代码是通过测试的了,所以运行起来应该是没有什么问题,如果有什么问题还望斧正。