1 实现思路概述 1.1 从ResultSet说起本文节选自《Spring 5核心原理》
说到ResultSet,有Java开发经验的“小伙伴”自然最熟悉不过了,不过我相信对于大多数人来说也算是“最熟悉的陌生人”。从ResultSet取值 *** 作大家都会,比如:
private static Listselect(String sql) { List result = new ArrayList<>(); Connection con = null; PreparedStatement pstm = null; ResultSet rs = null; try { //1. 加载驱动类 Class.forName("com.mysql.jdbc.Driver"); //2. 建立连接 con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/gp-vip-spring-db-demo", "root","123456"); //3. 创建语句集 pstm = con.prepareStatement(sql); //4. 执行语句集 rs = pstm.executeQuery(); while (rs.next()){ Member instance = new Member(); instance.setId(rs.getLong("id")); instance.setName(rs.getString("name")); instance.setAge(rs.getInt("age")); instance.setAddr(rs.getString("addr")); result.add(instance); } //5. 获取结果集 }catch (Exception e){ e.printStackTrace(); } //6. 关闭结果集、关闭语句集、关闭连接 finally { try { rs.close(); pstm.close(); con.close(); }catch (Exception e){ e.printStackTrace(); } } return result; }
以上我们在没有使用框架以前的常规 *** 作。随着业务和开发量的增加,在数据持久层这样的重复代码出现频次非常高。因此,我们就想到将非功能性代码和业务代码进行分离。我们首先想到将ResultSet封装数据的代码逻辑分离,增加一个mapperRow()方法,专门处理对结果的封装,代码如下:
private static Listselect(String sql) { List result = new ArrayList<>(); Connection con = null; PreparedStatement pstm = null; ResultSet rs = null; try { //1. 加载驱动类 Class.forName("com.mysql.jdbc.Driver"); //2. 建立连接 con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/gp-vip-spring-db-demo", "root","123456"); //3. 创建语句集 pstm = con.prepareStatement(sql); //4. 执行语句集 rs = pstm.executeQuery(); while (rs.next()){ Member instance = mapperRow(rs,rs.getRow()); result.add(instance); } //5. 获取结果集 }catch (Exception e){ e.printStackTrace(); } //6. 关闭结果集、关闭语句集、关闭连接 finally { try { rs.close(); pstm.close(); con.close(); }catch (Exception e){ e.printStackTrace(); } } return result; } private static Member mapperRow(ResultSet rs, int i) throws Exception { Member instance = new Member(); instance.setId(rs.getLong("id")); instance.setName(rs.getString("name")); instance.setAge(rs.getInt("age")); instance.setAddr(rs.getString("addr")); return instance; }
但在真实的业务场景中,这样的代码逻辑重复率实在太高,上面的改造只能应用Member类,换一个实体类又要重新封装,聪明的程序员肯定不会通过纯体力劳动给每一个实体类写一个mapperRow()方法,一定会想到代码复用方案。我们不妨来做这样一个改造。
先创建Member类:
package com.gupaoedu.vip.orm.demo.entity; import lombok.Data; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import java.io.Serializable; @Entity @Table(name="t_member") @Data public class Member implements Serializable { @Id private Long id; private String name; private String addr; private Integer age; @Override public String toString() { return "Member{" + "id=" + id + ", name='" + name + ''' + ", addr='" + addr + ''' + ", age=" + age + '}'; } }
优化JDBC *** 作:
public static void main(String[] args) { Member condition = new Member(); condition.setName("Tom"); condition.setAge(19); List> result = select(condition); System.out.println(Arrays.toString(result.toArray())); } private static List> select(Object condition) { List
上面巧妙地利用反射机制读取Class信息和Annotation信息,将数据库表中的列和类中的字段进行关联映射并赋值,以减少重复代码。
1.2 为什么需要ORM框架通过前面的讲解,我们已经了解ORM框架的基本实现原理。ORM是指对象关系映射(Object Relation Mapping),映射的不只是对象值,还有对象与对象之间的关系,例如一对多、多对多、一对一这样的表关系。现在市面上ORM框架也非常多,有大家所熟知的Hibernate、Spring JDBC、MyBatis、JPA等。在这里做一个简单的总结,如下表所示。
既然市面上有这么多选择,我为什么还要自己写 ORM框架呢?
这得从我的一次空降担任架构师的经验说起。空降面临最大的难题就是如何取得团队“小伙伴们”的信任。当时,团队总共就8人,每个人的水平参差不齐,甚至有些人还没接触过MySQL,诸如Redis等缓存中间件更不用说了。基本只会使用Hibernate的CRUD,而且已经影响到了系统性能。由于工期紧张,没有时间和精力给团队做系统培训,也为了兼顾可控性,于是就产生了自研ORM框架的想法。我做了这样的顶层设计,以降低团队“小伙伴们”的存息成本,顶层接口统一参数、统一返回值,具体如下。
**(1)规定查询方法的接口模型为: **
Listselect(QueryRule queryRule) throws Exception; Page> select(QueryRule queryRule,int pageNo,int pageSize) throws Exception; List
(2)规定删除方法的接口模型为:
boolean delete(T entity) throws Exception; int deleteAll(Listlist) throws Exception;
(3)规定插入方法的接口模型为:
PK insertAndReturnId(T entity) throws Exception; boolean insert(T entity) throws Exception; int insertAll(Listlist) throws Exception;
(4)规定修改方法的接口模型为:
boolean update(T entity) throws Exception;
利用这套基础的API,后面我又基于Redis、MongoDB、ElasticSearch、Hive、Hbase各封装了一套,以此来降低团队的学习成本,也大大提升了程序的可控性,更方便统一监控。
2 搭建基础架构 2.1 Page定义Page类的主要目的是为后面的分页查询统一返回结果做顶层支持,其主要功能包括分页逻辑的封装、分页数据。
package javax.core.common; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class Page2.2 ResultMsgimplements Serializable { private static final long serialVersionUID = 1L; private static final int DEFAULT_PAGE_SIZE = 20; private int pageSize = DEFAULT_PAGE_SIZE; //每页的记录数 private long start; //当前页第一条数据在List中的位置,从0开始 private List rows; //当前页中存放的记录,类型一般为List private long total; //总记录数 public Page() { this(0, 0, DEFAULT_PAGE_SIZE, new ArrayList ()); } public Page(long start, long totalSize, int pageSize, List rows) { this.pageSize = pageSize; this.start = start; this.total = totalSize; this.rows = rows; } public long getTotal() { return this.total; } public void setTotal(long total) { this.total = total; } public long getTotalPageCount() { if (total % pageSize == 0){ return total / pageSize; }else{ return total / pageSize + 1; } } public int getPageSize() { return pageSize; } public List getRows() { return rows; } public void setRows(List rows) { this.rows = rows; } public long getPageNo() { return start / pageSize + 1; } public boolean hasNextPage() { return this.getPageNo() < this.getTotalPageCount() - 1; } public boolean hasPreviousPage() { return this.getPageNo() > 1; } protected static int getStartOfPage(int pageNo) { return getStartOfPage(pageNo, DEFAULT_PAGE_SIZE); } public static int getStartOfPage(int pageNo, int pageSize) { return (pageNo - 1) * pageSize; } }
ResultMsg类主要是为统一返回结果做的顶层设计,主要包括状态码、结果说明内容和返回数据。
package javax.core.common; import java.io.Serializable; //底层设计 public class ResultMsg2.3 baseDaoimplements Serializable { private static final long serialVersionUID = 2635002588308355785L; private int status; //状态码,系统的返回码 private String msg; //状态码的解释 private T data; //放任意结果 public ResultMsg() {} public ResultMsg(int status) { this.status = status; } public ResultMsg(int status, String msg) { this.status = status; this.msg = msg; } public ResultMsg(int status, T data) { this.status = status; this.data = data; } public ResultMsg(int status, String msg, T data) { this.status = status; this.msg = msg; this.data = data; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
作为所有baseDao持久化框架的顶层接口,主要定义增、删、改、查统一的参数列表和返回值。
package javax.core.common.jdbc; import com.gupaoedu.vip.orm.framework.QueryRule; import javax.core.common.Page; import java.util.List; import java.util.Map; public interface baseDao2.4 QueryRule{ List select(QueryRule queryRule) throws Exception; Page> select(QueryRule queryRule,int pageNo,int pageSize) throws Exception; List > selectBySql(String sql, Object... args) throws Exception; Page > selectBySqlToPage(String sql, Object [] param, int pageNo, int pageSize) throws Exception; boolean delete(T entity) throws Exception; int deleteAll(List list) throws Exception; PK insertAndReturnId(T entity) throws Exception; boolean insert(T entity) throws Exception; int insertAll(List list) throws Exception; boolean update(T entity) throws Exception; }
如果用QueryRule类来构建查询条件,用户在做条件查询时不需要手写SQL,实现业务代码与SQL解耦。
package com.gupaoedu.vip.orm.framework; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public final class QueryRule implements Serializable { private static final long serialVersionUID = 1L; public static final int ASC_ORDER = 101; public static final int DESC_ORDER = 102; public static final int LIKE = 1; public static final int IN = 2; public static final int NOTIN = 3; public static final int BETWEEN = 4; public static final int EQ = 5; public static final int NOTEQ = 6; public static final int GT = 7; public static final int GE = 8; public static final int LT = 9; public static final int LE = 10; public static final int ISNULL = 11; public static final int ISNOTNULL = 12; public static final int ISEMPTY = 13; public static final int ISNOTEMPTY = 14; public static final int AND = 201; public static final int OR = 202; private List2.5 OrderruleList = new ArrayList (); private List queryRuleList = new ArrayList (); private String propertyName; private QueryRule() {} private QueryRule(String propertyName) { this.propertyName = propertyName; } public static QueryRule getInstance() { return new QueryRule(); } public QueryRule addAscOrder(String propertyName) { this.ruleList.add(new Rule(ASC_ORDER, propertyName)); return this; } public QueryRule addDescOrder(String propertyName) { this.ruleList.add(new Rule(DESC_ORDER, propertyName)); return this; } public QueryRule andIsNull(String propertyName) { this.ruleList.add(new Rule(ISNULL, propertyName).setAndOr(AND)); return this; } public QueryRule andIsNotNull(String propertyName) { this.ruleList.add(new Rule(ISNOTNULL, propertyName).setAndOr(AND)); return this; } public QueryRule andIsEmpty(String propertyName) { this.ruleList.add(new Rule(ISEMPTY, propertyName).setAndOr(AND)); return this; } public QueryRule andIsNotEmpty(String propertyName) { this.ruleList.add(new Rule(ISNOTEMPTY, propertyName).setAndOr(AND)); return this; } public QueryRule andLike(String propertyName, Object value) { this.ruleList.add(new Rule(LIKE, propertyName, new Object[] { value }).setAndOr(AND)); return this; } public QueryRule andEqual(String propertyName, Object value) { this.ruleList.add(new Rule(EQ, propertyName, new Object[] { value }).setAndOr(AND)); return this; } public QueryRule andBetween(String propertyName, Object... values) { this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(AND)); return this; } public QueryRule andIn(String propertyName, List
Order类主要用于封装排序规则,代码如下:
package com.gupaoedu.vip.orm.framework; public class Order { private boolean ascending; //升序还是降序 private String propertyName; //哪个字段升序,哪个字段降序 public String toString() { return propertyName + ' ' + (ascending ? "asc" : "desc"); } protected Order(String propertyName, boolean ascending) { this.propertyName = propertyName; this.ascending = ascending; } public static Order asc(String propertyName) { return new Order(propertyName, true); } public static Order desc(String propertyName) { return new Order(propertyName, false); } }
因篇幅原因,具体的 *** 作类下一篇继续。
关注微信公众号『 Tomd架构 』回复“Spring”可获取完整源码。
本文为“Tomd架构”原创,转载请注明出处。技术在于分享,我分享我快乐!
如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tomd架构 』可获取更多技术干货!
原创不易,坚持很酷,都看到这里了,小伙伴记得点赞、收藏、在看,一键三连加关注!如果你觉得内容太干,可以分享转发给朋友滋润滋润!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)