30个类手写Spring核心原理之自定义ORM(上)(6)

//1. 加载驱动类

Class.forName(“com.mysql.jdbc.Driver”);

//2. 建立连接

con = DriverManager.getConnection(“jdbc:mysql://127.0.0.1:3306/gp-vip-spring-db-demo? characterEncoding=UTF-8&rewriteBatchedStatements=true”,“root”,“123456”);

//根据类名找属性名

Map<String,String> columnMapper = new HashMap<String,String>();

//根据属性名找字段名

Map<String,String> fieldMapper = new HashMap<String,String>();

Field[] fields = entityClass.getDeclaredFields();

for (Field field : fields) {

field.setAccessible(true);

String fieldName = field.getName();

if(field.isAnnotationPresent(Column.class)){

Column column = field.getAnnotation(Column.class);

String columnName = column.name();

columnMapper.put(columnName,fieldName);

fieldMapper.put(fieldName,columnName);

}else {

//默认就是字段名、属性名一致

columnMapper.put(fieldName, fieldName);

fieldMapper.put(fieldName,fieldName);

}

}

//3. 创建语句集

Table table = entityClass.getAnnotation(Table.class);

String sql = "select * from " + table.name();

StringBuffer where = new StringBuffer(" where 1=1 ");

for (Field field : fields) {

Object value =field.get(condition);

if(null != value){

if(String.class == field.getType()) {

where.append(" and " + fieldMapper.get(field.getName()) + " = ‘" + value + "’");

}else{

where.append(" and " + fieldMapper.get(field.getName()) + " = " + value + “”);

}

//其他的在这里就不一一列举,后面我们手写ORM框架时会完善

}

}

System.out.println(sql + where.toString());

pstm = con.prepareStatement(sql + where.toString());

//4. 执行语句集

rs = pstm.executeQuery();

//元数据?

//保存了处理真正数值以外的所有附加信息

int columnCounts = rs.getMetaData().getColumnCount();

while (rs.next()){

Object instance = entityClass.newInstance();

for (int i = 1; i <= columnCounts; i++) {

//实体类属性名,对应数据库表的字段名

//可以通过反射机制拿到实体类的所有字段

//从rs中取得当前这个游标下的类名

String columnName = rs.getMetaData().getColumnName(i);

//有可能是私有的

Field field = entityClass.getDeclaredField(columnMapper.get(columnName));

field.setAccessible(true);

field.set(instance,rs.getObject(columnName));

}

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;

}

上面巧妙地利用反射机制读取Class信息和Annotation信息,将数据库表中的列和类中的字段进行关联映射并赋值,以减少重复代码。

1.2 为什么需要ORM框架

通过前面的讲解,我们已经了解ORM框架的基本实现原理。ORM是指对象关系映射(Object Relation Mapping),映射的不只是对象值,还有对象与对象之间的关系,例如一对多、多对多、一对一这样的表关系。现在市面上ORM框架也非常多,有大家所熟知的Hibernate、Spring JDBC、MyBatis、JPA等。在这里做一个简单的总结,如下表所示。

| 名称 | 特征 | 描述 |

| — | — | — |

| Hibernate | 全自动(挡) | 不需要写一句SQL |

| MyBatis | 半自动(挡) | 手自一体,支持简单的映射,复杂关系需要自己写SQL |

| Spring JDBC | 纯手动(挡) | 所有的SQL都要自己写,它帮我们设计了一套标准流程 |

既然市面上有这么多选择,我为什么还要自己写 ORM框架呢?

这得从我的一次空降担任架构师的经验说起。空降面临最大的难题就是如何取得团队“小伙伴们”的信任。当时,团队总共就8人,每个人的水平参差不齐,甚至有些人还没接触过MySQL,诸如Redis等缓存中间件更不用说了。基本只会使用Hibernate的CRUD,而且已经影响到了系统性能。由于工期紧张,没有时间和精力给团队做系统培训,也为了兼顾可控性,于是就产生了自研ORM框架的想法。我做了这样的顶层设计,以降低团队“小伙伴们”的存息成本,顶层接口统一参数、统一返回值,具体如下。

**(1)规定查询方法的接口模型为: **

/**

  • 获取列表

  • @param queryRule 查询条件

  • @return

*/

List select(QueryRule queryRule) throws Exception;

/**

  • 获取分页结果

  • @param queryRule 查询条件

  • @param pageNo 页码

  • @param pageSize 每页条数

  • @return

*/

Page<?> select(QueryRule queryRule,int pageNo,int pageSize) throws Exception;

/**

  • 根据SQL获取列表

  • @param sql SQL语句

  • @param args 参数

  • @return

*/

List<Map<String,Object>> selectBySql(String sql, Object… args) throws Exception;

/**

  • 根据SQL获取分页

  • @param sql SQL语句

  • @param pageNo 页码

  • @param pageSize 每页条数

  • @return

*/

Page<Map<String,Object>> selectBySqlToPage(String sql, Object [] param, int pageNo, int pageSize) throws Exception;

(2)规定删除方法的接口模型为:

/**

  • 删除一条记录

  • @param entity entity中的ID不能为空,如果ID为空,其他条件不能为空,都为空不予执行

  • @return

*/

boolean delete(T entity) throws Exception;

/**

  • 批量删除

  • @param list

  • @return 返回受影响的行数

  • @throws Exception

*/

int deleteAll(List list) throws Exception;

(3)规定插入方法的接口模型为:

/**

  • 插入一条记录并返回插入后的ID

  • @param entity 只要entity不等于null,就执行插入

  • @return

*/

PK insertAndReturnId(T entity) throws Exception;

/**

  • 插入一条记录自增ID

  • @param entity

  • @return

  • @throws Exception

*/

boolean insert(T entity) throws Exception;

/**

  • 批量插入

  • @param list

  • @return 返回受影响的行数

  • @throws Exception

*/

int insertAll(List list) throws Exception;

(4)规定修改方法的接口模型为:

/**

  • 修改一条记录

  • @param entity entity中的ID不能为空,如果ID为空,其他条件不能为空,都为空不予执行

  • @return

  • @throws Exception

*/

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;

/**

  • 分页对象,包含当前页数据及分页信息,如总记录数

  • 能够支持和JQuery EasyUI直接对接,能够支持和BootStrap Table直接对接

*/

public class Page implements 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());

}

/**

  • 默认构造方法

  • @param start 本页数据在数据库中的起始位置

  • @param totalSize 数据库中总记录条数

  • @param pageSize 本页容量

  • @param rows 本页包含的数据

*/

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;

}

/**

  • 取该页的当前页码,页码从1开始

*/

public long getPageNo() {

return start / pageSize + 1;

}

/**

  • 该页是否有下一页

*/

public boolean hasNextPage() {

return this.getPageNo() < this.getTotalPageCount() - 1;

}

/**

  • 该页是否有上一页

*/

public boolean hasPreviousPage() {

return this.getPageNo() > 1;

}

/**

  • 获取任意一页第一条数据在数据集中的位置,每页条数使用默认值

  • @see #getStartOfPage(int,int)

*/

protected static int getStartOfPage(int pageNo) {

return getStartOfPage(pageNo, DEFAULT_PAGE_SIZE);

}

/**

  • 获取任意一页第一条数据在数据集中的位置

  • @param pageNo 从1开始的页号

  • @param pageSize 每页记录条数

  • @return 该页第一条数据

*/

public static int getStartOfPage(int pageNo, int pageSize) {

return (pageNo - 1) * pageSize;

}

}

2.2 ResultMsg

ResultMsg类主要是为统一返回结果做的顶层设计,主要包括状态码、结果说明内容和返回数据。

package javax.core.common;

import java.io.Serializable;

//底层设计

public class ResultMsg implements 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;

}

}

2.3 BaseDao

作为所有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 BaseDao<T,PK> {

/**

  • 获取列表

  • @param queryRule 查询条件

  • @return

*/

List select(QueryRule queryRule) throws Exception;

/**

  • 获取分页结果

  • @param queryRule 查询条件

  • @param pageNo 页码

  • @param pageSize 每页条数

  • @return

*/

Page<?> select(QueryRule queryRule,int pageNo,int pageSize) throws Exception;

/**

  • 根据SQL获取列表

  • @param sql SQL语句

  • @param args 参数

  • @return

*/

List<Map<String,Object>> selectBySql(String sql, Object… args) throws Exception;

/**

  • 根据SQL获取分页

  • @param sql SQL语句

  • @param pageNo 页码

  • @param pageSize 每页条数

  • @return

*/

Page<Map<String,Object>> selectBySqlToPage(String sql, Object [] param, int pageNo, int pageSize) throws Exception;

/**

  • 删除一条记录

  • @param entity entity中的ID不能为空,如果ID为空,其他条件不能为空,都为空则不予执行

  • @return

*/

boolean delete(T entity) throws Exception;

/**

  • 批量删除

  • @param list

  • @return 返回受影响的行数

  • @throws Exception

*/

int deleteAll(List list) throws Exception;

/**

  • 插入一条记录并返回插入后的ID

  • @param entity 只要entity不等于null,就执行插入操作

  • @return

*/

PK insertAndReturnId(T entity) throws Exception;

/**

  • 插入一条记录自增ID

  • @param entity

  • @return

  • @throws Exception

*/

boolean insert(T entity) throws Exception;

/**

  • 批量插入

  • @param list

  • @return 返回受影响的行数

  • @throws Exception

*/

int insertAll(List list) throws Exception;

/**

  • 修改一条记录

  • @param entity entity中的ID不能为空,如果ID为空,其他条件不能为空,都为空则不予执行

  • @return

  • @throws Exception

*/

boolean update(T entity) throws Exception;

}

2.4 QueryRule

如果用QueryRule类来构建查询条件,用户在做条件查询时不需要手写SQL,实现业务代码与SQL解耦。

package com.gupaoedu.vip.orm.framework;

import java.io.Serializable;

import java.util.ArrayList;

import java.util.List;

/**

  • QueryRule,主要功能用于构造查询条件

*/

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 List ruleList = 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();

}

/**

  • 添加升序规则

  • @param propertyName

  • @return

*/

public QueryRule addAscOrder(String propertyName) {

this.ruleList.add(new Rule(ASC_ORDER, propertyName));

return this;

}

/**

  • 添加降序规则

  • @param propertyName

  • @return

*/

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 values) {

this.ruleList.add(new Rule(IN, propertyName, new Object[] { values }).setAndOr(AND));

return this;

}

public QueryRule andIn(String propertyName, Object… values) {

this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(AND));

return this;

}

public QueryRule andNotIn(String propertyName, List values) {

this.ruleList.add(new Rule(NOTIN, propertyName, new Object[] { values }).setAndOr(AND));

return this;

}

public QueryRule orNotIn(String propertyName, Object… values) {

this.ruleList.add(new Rule(NOTIN, propertyName, values).setAndOr(OR));

return this;

}

public QueryRule andNotEqual(String propertyName, Object value) {

this.ruleList.add(new Rule(NOTEQ, propertyName, new Object[] { value }).setAndOr(AND));

return this;

}

public QueryRule andGreaterThan(String propertyName, Object value) {

this.ruleList.add(new Rule(GT, propertyName, new Object[] { value }).setAndOr(AND));

return this;

}

public QueryRule andGreaterEqual(String propertyName, Object value) {

this.ruleList.add(new Rule(GE, propertyName, new Object[] { value }).setAndOr(AND));

return this;

}

public QueryRule andLessThan(String propertyName, Object value) {

this.ruleList.add(new Rule(LT, propertyName, new Object[] { value }).setAndOr(AND));

return this;

}

public QueryRule andLessEqual(String propertyName, Object value) {

this.ruleList.add(new Rule(LE, propertyName, new Object[] { value }).setAndOr(AND));

return this;

}

public QueryRule orIsNull(String propertyName) {

this.ruleList.add(new Rule(ISNULL, propertyName).setAndOr(OR));

return this;

}

public QueryRule orIsNotNull(String propertyName) {

this.ruleList.add(new Rule(ISNOTNULL, propertyName).setAndOr(OR));

return this;

}

public QueryRule orIsEmpty(String propertyName) {

this.ruleList.add(new Rule(ISEMPTY, propertyName).setAndOr(OR));

return this;

}

public QueryRule orIsNotEmpty(String propertyName) {

this.ruleList.add(new Rule(ISNOTEMPTY, propertyName).setAndOr(OR));

return this;

}

public QueryRule orLike(String propertyName, Object value) {

this.ruleList.add(new Rule(LIKE, propertyName, new Object[] { value }).setAndOr(OR));

return this;

}

public QueryRule orEqual(String propertyName, Object value) {

this.ruleList.add(new Rule(EQ, propertyName, new Object[] { value }).setAndOr(OR));

return this;

}

public QueryRule orBetween(String propertyName, Object… values) {

this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(OR));

return this;

}

public QueryRule orIn(String propertyName, List values) {

this.ruleList.add(new Rule(IN, propertyName, new Object[] { values }).setAndOr(OR));

return this;

}

public QueryRule orIn(String propertyName, Object… values) {

this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(OR));

总结

阿里伤透我心,疯狂复习刷题,终于喜提offer 哈哈~好啦,不闲扯了

image

1、JAVA面试核心知识整理(PDF):包含JVMJAVA集合JAVA多线程并发,JAVA基础,Spring原理微服务,Netty与RPC,网络,日志,ZookeeperKafka
作者私人号:vip1024c
RabbitMQ,Hbase,MongoDB,Cassandra,设计模式负载均衡数据库一致性哈希JAVA算法数据结构,加密算法,分布式缓存,Hadoop,Spark,Storm,YARN,机器学习,云计算共30个章节。

image

2、Redis学习笔记及学习思维脑图

image

3、数据面试必备20题+数据库性能优化的21个最佳实践

image
eturn this;

}

public QueryRule orLike(String propertyName, Object value) {

this.ruleList.add(new Rule(LIKE, propertyName, new Object[] { value }).setAndOr(OR));

return this;

}

public QueryRule orEqual(String propertyName, Object value) {

this.ruleList.add(new Rule(EQ, propertyName, new Object[] { value }).setAndOr(OR));

return this;

}

public QueryRule orBetween(String propertyName, Object… values) {

this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(OR));

return this;

}

public QueryRule orIn(String propertyName, List values) {

this.ruleList.add(new Rule(IN, propertyName, new Object[] { values }).setAndOr(OR));

return this;

}

public QueryRule orIn(String propertyName, Object… values) {

this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(OR));

总结

阿里伤透我心,疯狂复习刷题,终于喜提offer 哈哈~好啦,不闲扯了

[外链图片转存中…(img-blhKkIrF-1721043796273)]

1、JAVA面试核心知识整理(PDF):包含JVMJAVA集合JAVA多线程并发,JAVA基础,Spring原理微服务,Netty与RPC,网络,日志,ZookeeperKafka
作者私人号:vip1024c
RabbitMQ,Hbase,MongoDB,Cassandra,设计模式负载均衡数据库一致性哈希JAVA算法数据结构,加密算法,分布式缓存,Hadoop,Spark,Storm,YARN,机器学习,云计算共30个章节。

[外链图片转存中…(img-oOqsrwkf-1721043796274)]

2、Redis学习笔记及学习思维脑图

[外链图片转存中…(img-mUFkwkUx-1721043796274)]

3、数据面试必备20题+数据库性能优化的21个最佳实践

[外链图片转存中…(img-PCfOLyrg-1721043796275)]

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值