本周我们在上周SORMv1.0框架的基础上对其进行升级,加入了一些设计模式,连接池等改造,大大的提高了整个框架运行的效率,得到现在的SORMv1.8版本。
SORMv1.8的完全版本的获取链接为:
链接:https://pan.baidu.com/s/1r9aCf3IUg4uH_Lj9OZIO6g 提取码:a4r8
与此同时,我们结束了第一阶段的java学习,开始进入数据库的学习阶段。
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)注释
单行注释:#注释文字
单行注释:-- 注释文字(注意在两个单横线之后有一个空格)
多行注释:/* 注释文字 */