第34次文章:SORM框架(四)
- 2019 年 10 月 8 日
- 筆記
本周我们在上周SORMv1.0框架的基础上对其进行升级,加入了一些设计模式,连接池等改造,大大的提高了整个框架运行的效率,得到现在的SORMv1.8版本。
SORMv1.8的完全版本的获取链接为:
链接:https://pan.baidu.com/s/1r9aCf3IUg4uH_Lj9OZIO6g 提取码:a4r8
与此同时,我们结束了第一阶段的java学习,开始进入数据库的学习阶段。
一、对SORM进行升级改造
1.对query进行简化操作
我们在编写SORMv1.0时,构建了一个MySqlQuery类,实现了Query接口。但是经过分析之后,MySqlQuery类中的每个方法的实现并不仅限于MySql数据库的操作,对于其他数据库也是可以使用这些方法进行操作的。所以我们将Query接口改变为一个抽象类,将之前所有MySqlQuery类中实现的方法全部搬移到Query抽象类中直接进行实现。同时,由于每种不同的数据库会具有不同的分页查询方法,所以我们在Query类中,增加一个分页查询抽象方法。提供给每个不同的数据库方法进行单独实现。
/** * 分页查询 * @param pageNum 第几页数据 * @param size 每页显示多少记录 * @return */ public abstract Object queryPagenate(int pageNum,int size);
由于当前的SORM数据库框架并没有涉及到分页部分,所以我们现在仅仅是为以后预留出此接口,并不进行实现。
2.使用模板方法简化Query
当我们分析一下Query类中的queryRows和queryValue方法时,我们会发现两者的前半部分都是相同的,均为先获取与数据库的连接,然后传入sql语句,给sql语句设置参数,不同的地方在于两者的查询方式。所以我们使用模板方法模式,新建一个模板方法excueteQueryTemplate,将相同的部分一起进行实现,不同的部分,我们使用回调的方式,在各自的方法中进行实现。具体的实现如下:
/** * 采用模板方法模式将JDBC操作封装成模板,便于重用 * @param sql sql语句 * @param params sql的参数 * @param clazz 记录要封装到的java类 * @param back CallBack的实现类,实现回调 * @return */ public Object excueteQueryTemplate(String sql,Object[] params,Class clazz,CallBack back) { Connection conn = DBManager.getConn(); List list = null; //存储查询结果的容器 PreparedStatement ps = null; ResultSet rs = null; try { ps = (PreparedStatement) conn.prepareStatement(sql); //给sql设置参数 JDBCUtils.handleParams(ps, params); System.out.println(ps); rs = ps.executeQuery(); return back.doExecute(conn, ps, rs); } catch (Exception e) { e.printStackTrace(); return null; } finally { DBManager.close(ps, conn); } }
为了实现回调,我们新建一个CallBack接口,然后增加一个doExcute方法,提供给后续的每个方法进行自主实现。
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; public interface CallBack { public Object doExecute(Connection conn,PreparedStatement ps,ResultSet rs); }
经过模板方法模式的改造之后,我们可以看到queryRows和queryValue方法的代码如下:
/** * 查询返回多行记录,并将每行记录封装到clazz指定的类的对象中 * @param sql 查询语句 * @param clazz 封装数据的javabean类的class对象 * @param params sql的参数 * @return 查询到的结果 */ public List queryRows(final String sql,final Class clazz,final Object[] params) { return (List)excueteQueryTemplate(sql, params, clazz, new CallBack() { @Override public Object doExecute(Connection conn, java.sql.PreparedStatement ps, ResultSet rs) { List list = null; try { ResultSetMetaData metaData = rs.getMetaData(); //多行 while(rs.next()) { if(list==null) { list = new ArrayList(); } Object rowObj = clazz.newInstance(); //调用Javabean的无参构造器 //多列 select username,pwd,age from user where id>? and age>18 for(int i=0;i<metaData.getColumnCount();i++) { String columnName = metaData.getColumnLabel(i+1); Object columnValue = rs.getObject(i+1); //调用rowObject对象的setUsername方法,将ColumnValue的值设置进去 ReflectUtils.invokeSet(rowObj, columnName, columnValue); } list.add(rowObj); } }catch(Exception e) { e.printStackTrace(); } return list; } }); } /** * 查询返回一个值(一行一列),并将该值返回 * @param sql 查询语句 * @param params sql的参数 * @return 查询到的结果 */ public Object queryValue(String sql,Object[] params) { return excueteQueryTemplate(sql, params, null, new CallBack() { @Override public Object doExecute(Connection conn, java.sql.PreparedStatement ps, ResultSet rs) { Object value = null; try { while(rs.next()) { value = rs.getObject(1); } } catch (SQLException e) { e.printStackTrace(); } return value; } }); }
tips:使用模板方法模式之后,可以避免我们在两个方法中写重复性代码。我们都是通过调用回调函数CallBack对两个方法进行具体实现。在回调函数中,我们使用匿名内部类,直接对CallBack接口中的doExcute方法进行实现。大大简化了代码,使得代码具有更强的阅读性。
3.增加连接池(Connection Pool)
在我们学习连接的时候就已经知道,获取connection对象的底层实现是利用Socket套接字对象,是十分耗时的一项操作。所以在实际的使用中,我们对于connection的使用应该尽可能的节省。对connection对象进行重复操作,可以大大提高整个系统的效率。所以我们建立连接池对象的基本思想如下:
(1)将Connection对象放入List中,反复重用。
(2)在连接池的初始化的时候,事先放入多个连接池对象。当我们从连接池中取连接对象时,如果池中有可用连接,则将池中最后一个返回,同时,将该连接从池中remove,表示正在使用。如果池中无可用连接,则创建一个新的。
(3)在关闭连接时,不是真正关闭连接,而是将用完的连接放入池中。
所以根据上面我们对连接池实现思路,创建一个连接池类,具体实现如下:
package com.peng.sorm.pool; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import com.peng.sorm.core.DBManager; /** * 连接池的类 */ public class DBConnPool { /** * 连接池对象 */ private static List<Connection> pool; /** * 最大连接数 */ private static final int POOL_MAX_SIZE=DBManager.getConf().getPoolMaxSize(); /** * 最小连接数 */ private static final int POOL_MIN_SIZE=DBManager.getConf().getPoolMinSize(); /** * 初始化连接池,使池中连接数达到最小值 */ public void initPool() { if(pool == null) { pool = new ArrayList<Connection>(); } while(pool.size()< POOL_MIN_SIZE) { pool.add(DBManager.createConn()); System.out.println("初始化池,池中连接数:"+pool.size()); } } /** * 从池中取出一个连接 * @return */ public synchronized Connection getConnection() { int last_index = pool.size()-1; Connection conn = pool.get(last_index); pool.remove(last_index); return conn; } /** * 所谓的关闭连接并不是真的连接,只是将此连接放回到连接池中 * @param conn */ public synchronized void close(Connection conn) { if(pool.size()>=POOL_MAX_SIZE) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }else { pool.add(conn); } } public DBConnPool() { initPool(); } }
tips:由于每个客户的用途有所差异,所以对连接池的大小需求也有所不同。为了避免用户重新修改我们的代码。我们可以在配置文件中增设连接池的最大最小值选项,然后我们在代码中就可以直接通过配置文件来获取我们需要的值。从而避免了客户修改代码的风险。
测试连接池效率:
import java.util.List; import com.peng.sorm.core.Query; import com.peng.sorm.core.QueryFactory; import com.peng.vo.EmpVO; /** * 测试连接池的效率 * */ public class Test2 { public static void test() { Query q = QueryFactory.createQuery(); //复杂查询,将两个表进行关联查询 String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e " +"join dept d on e.deptId=d.id "; List<EmpVO> list2 = q.queryRows(sql2,EmpVO.class, null); for(EmpVO e:list2) { System.out.println(e.getEmpname()+" "+e.getAge()+" "+e.getId()+" "+e.getXinshui()+" "+e.getDeptName()+" "+e.getDeptAddr()); System.out.println("******************"); } } public static void main(String[] args) { long a = System.currentTimeMillis(); for(int i=0;i<3000;i++) { test(); } long b = System.currentTimeMillis(); System.out.println(b-a);//不加连接池:14187毫秒. 增加连接池之后:1957 } }
在使用连接池和不使用连接池时,最后的耗时结果如下
不使用连接池:

使用连接池:

tips:经过两者的对比之后就可以发现,提高的效率不是一点点,而是接近10倍。未使用的时候,耗时14187ms,使用连接池的时候,耗时1957ms,由此可见使用连接池对象时的优越性。
以上就是关于JAVA的SORM基础啦!下面我们进入数据库的正式学习!
二、数据库
1.相关概念
DB:数据库(datebase)存储数据的“仓库”。它保存了一系列有组织的数据。
DBMS:数据库管理系统(Datebase Management System)。数据库是通过DBMS创建和操作的容器
SQL:结构化查询语言(Structure Query Language):专门用来与数据库通信的语言
SQL的优点
(1)不是某个特定数据库供应商专用的语言,几乎所有DBMS都支持SQL
(2)简单易学
(3)虽然简单,但实际上是一种强有力的语言,灵活使用其语言元素,可以进行非常复杂和高级的数据库操作。
2.数据库的特点
(1)将数据放到表中,表再放到库中
(2)一个数据库中可以有多个表,每个表都有一个名字,用来标识自己。表名具有唯一性
(3)表具有一些特性,这些特定定义了数据在表中如何存储,类似于java中“类”的设计
(4)表由列组成,我们也称为字段。所有表都是由一个或多个列组成的,每一列类似java中的”属性“。
(5)表中的数据是按行存储的,每一行类似于java中的“对象”。
3.MySQL服务的启动和停止
方式一:计算机-右击管理-服务
方式二:通过管理员身份运行cmd窗口
net statrt 服务名(启动服务)
net stop 服务名(停止服务)
4.MySQL服务的登录和退出
方式一:通过mysql自带的客户端 只限于root用户
方式二:通过Windows自带的客户端
登录:mysql -h主机名 -P端口号 -u用户名 -p密码
退出:exit 或 ctrl+c
5.MySQL的常见命令
(1)查看当前所有的数据库
show databases;
(2)打开指定的库
use 库名;
(3)查看当前库中所有的表
show tables;
(4)查看其他库中的表
show tables from 库名;
(5)创建表
create table 表名(
列名 列类型,
列名 列类型,
………
);
(6)查看表结构
desc 表名;
(7)查看服务器的版本
方式一:登录到mysql服务器
select version();
方式二:没有登录到mysql服务端
mysql –version 或 mysql –V
6.MySQL的语法规范
(1)不区分大小写,但是建议关键字大写,表名、列名小写
(2)每条命令最好用分号结尾
(3)每条命令根据需要,可以进行缩进 或换行
(4)注释
单行注释:#注释文字
单行注释:— 注释文字(注意在两个单横线之后有一个空格)
多行注释:/* 注释文字 */