}
//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.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;
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
结局:总结+分享
看完美团、字节、腾讯这三家的一二三面试问题,是不是感觉问的特别多,可能咱们真的又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。
开篇有提及我可是足足背下了Java互联网工程师面试1000题,多少还是有点用的呢,换汤不换药,不管面试官怎么问你,抓住本质即可!能读到此处的都是真爱
- Java互联网工程师面试1000题
而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的 《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题。
- 程序员代码面试指南–IT名企算法与数据结构题目最优解
- 其余像设计模式,建议可以看看下面这4份PDF(已经整理)
- 更多的Java面试学习笔记如下,关于面试这一块,我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。
以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
.(img-iWms6DfM-1712133184700)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-cceyNeAQ-1712133184700)]
结局:总结+分享
看完美团、字节、腾讯这三家的一二三面试问题,是不是感觉问的特别多,可能咱们真的又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。
开篇有提及我可是足足背下了Java互联网工程师面试1000题,多少还是有点用的呢,换汤不换药,不管面试官怎么问你,抓住本质即可!能读到此处的都是真爱
- Java互联网工程师面试1000题
[外链图片转存中…(img-ws1PqpaC-1712133184700)]
而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的 《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题。
- 程序员代码面试指南–IT名企算法与数据结构题目最优解
[外链图片转存中…(img-viwHjvKu-1712133184701)]
- 其余像设计模式,建议可以看看下面这4份PDF(已经整理)
[外链图片转存中…(img-SRarHfPV-1712133184702)]
- 更多的Java面试学习笔记如下,关于面试这一块,我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。
[外链图片转存中…(img-7F6ydA8B-1712133184702)]
以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算