博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA开发之简化Dao层、提高开发效率
阅读量:6869 次
发布时间:2019-06-26

本文共 17402 字,大约阅读时间需要 58 分钟。

通常我们在开发Java企业级应用的时候使用的技术大部分是Spring、Hibernate、mybatis、Struts2等。尤其是Spring,相信这个庞大而优雅的技术体系你已经离不开了,在我们项目代码中基本是骨干力量的存在。

而我们使用的ORM框架大多数也是Hibernate、mybatis或者Spring提供的jdbc简单封装的JdbcTemplate。如果我们的项目组开发人员对所选型的ORM框架很熟悉并能熟练使用是再好不过了,但是大部分情况下项目组成员的技术水平是参差不齐的,有时会踩到所选型ORM框架的坑。比如Hibernate很容易引起性能问题,mybatis在重构时很麻烦等等增加了项目的交付风险。

本文希望能在Dao层的开发上做的简单,健壮,提高开发效率:

1 对JdbcTemplate做一层简单的封装,能够对单表的CRUD做到无需写SQL语句

2 在重构SQL代码时对SQL层做少量修改或者不修改

我们开始吧!

我们先考虑一下:如果做到CRUD的不需要写SQL,这也意味着SQL是要自动生成的。

我们先看SQL语句的组成:

1 添加语句: insert into tableName (id, name, ...) values (1, 'name', ...);

2 修改语句:update tableName set name = 'name', age = 'age' where id = 'id';

3 删除语句:delete from tableName where id = 'id';

4 查询语句:select id, name from tableName where age > 18;

一般我们做JAVA企业级开发都会有领域模型的概念,这个领域模型会对应一个数据库表并包括这个数据库表的所有字段。那么这时我们可以利用领域模型的字段生成对应的SQL语句。这里我们先借用JPA的注解完成领域模型属性与数据库表的映射,熟悉hibernate的朋友一定不会陌生,当然你也可以自己定义注解。

如下领域模型:

package com.applet.model;import com.fasterxml.jackson.databind.annotation.JsonSerialize;import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;import com.applet.base.BaseModel;import com.applet.enumeration.YesNoEnum;import com.applet.utils.DateUtils;import javax.persistence.*;import java.io.Serializable;import java.util.Date;@Entity@Table(name = "TS_ROLE")public class Role implements Serializable {    /**     * 

* Field serialVersionUID: 序列号 *

*/ private static final long serialVersionUID = 1L; //主键 @Id @Column @JsonSerialize(using = ToStringSerializer.class) protected Long id; // 名称 @Column private String name; // 状态(1启用,2停用) @Column private Integer state; // 创建人id @Column @JsonSerialize(using = ToStringSerializer.class) private Long createId; // 创建日期 @Temporal(TemporalType.TIMESTAMP) @Column private Date createTime; // 是否系统角色(1是,2否) @Column private Integer isSys; // 描述 @Column private String remark; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getStateStr() { return YesNoEnum.valueOfValidateLabel(state); } public String getIsSysStr() { return YesNoEnum.valueOf(isSys); } public String getCreateTimeStr() { if (createTime != null) { return DateUtils.dateTimeToString(createTime); } return null; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getState() { return state; } public void setState(Integer state) { this.state = state; } public Long getCreateId() { return createId; } public void setCreateId(Long createId) { this.createId = createId; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Integer getIsSys() { return isSys; } public void setIsSys(Integer isSys) { this.isSys = isSys; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; }}复制代码

我们的基础的dao接口如下:

package com.applet.base;import java.io.Serializable;import java.util.List;public interface BaseDao
{ /** * 判断某一字段是否重复 * @param id 实体id * @param filedValue 字段值 * @param fieldName 字段名称 * @return */ //public boolean isDuplicateField(PK id, Object filedValue, String fieldName); /** *

Description: 添加实体

* @param t 实体对象 */ public int insert(T t); /** *

Description: 批量添加实体

* @param list 实体对象列表 */ public int batchInsert(final List
list); /** *

Description: 更新实体,字段值为null的不更新

* @param t 实体对象 */ public int update(T t); /** *

Description: 更新实体

* @param t 实体对象 */ public int updateForce(T t); /** *

Description: 根据id删除实体

* @param id 实体id值 */ public int delete(PK id); /** *

Description: 批量删除实体

* @param ids 实体id值数组 */ public int delete(PK[] ids); /** *

Description: 按条件查询实体列表

* @param wb QueryCondition对象 * @return 实体列表 */ //public List
query(QueryCondition wb); /** *

Description: 按条件查询实体数量

* @param wb QueryCondition对象 * @return 实体数量 */ //public int count(QueryCondition wb); /** *

Description: 根据id查询实体

* @param id 实体id值 * @return 实体对象 */ public T load(PK id); /** *

Description: 按条件删除实体

* @param wb QueryCondition对象 */ //public int deleteByCondition(QueryCondition wb); /** *

Description: 分页查询

* @param wb QueryCondition对象 * @return */ //public Page
queryPage(QueryCondition wb);}复制代码

我们基础的dao接口实现如下:

package com.applet.base;import com.applet.sql.builder.SelectBuilder;import com.applet.sql.builder.WhereBuilder;import com.applet.sql.mapper.DefaultRowMapper;import com.applet.sql.page.PageSql;import com.applet.sql.record.DomainModelAnalysis;import com.applet.sql.record.DomainModelContext;import com.applet.sql.record.ExtendType;import com.applet.sql.record.TableColumn;import com.applet.sql.type.JdbcType;import com.applet.sql.type.TypeHandler;import com.applet.sql.type.TypeHandlerRegistry;import com.applet.utils.KeyUtils;import com.applet.utils.Page;import com.applet.utils.SpringContextHelper;import org.apache.commons.lang3.StringUtils;import org.apache.log4j.Logger;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.dao.support.DataAccessUtils;import org.springframework.jdbc.core.BatchPreparedStatementSetter;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.util.ReflectionUtils;import java.io.Serializable;import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.sql.PreparedStatement;import java.sql.SQLException;import java.util.ArrayList;import java.util.List;import java.util.Map;public class BaseDaoImpl
implements BaseDao
, InitializingBean { protected static final Logger log = Logger.getLogger(BaseDaoImpl.class); @Autowired protected JdbcTemplate jdbcTemplate; @Autowired protected PageSql pageSql; protected Class
modelClass; protected DomainModelAnalysis domainModelAnalysis; public BaseDaoImpl() { } @SuppressWarnings("unchecked") protected Class
autoGetDomainClass() { if (modelClass == null) { Type type = this.getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { modelClass = (Class
) ((ParameterizedType) type).getActualTypeArguments()[0]; } else { throw new RuntimeException("SubClass must give the ActualTypeArguments"); } } return modelClass; } /** * 获取添加实体的SQL语句 * * @return */ protected String getInsertSql() { String[] array = domainModelAnalysis.joinColumnWithPlaceholder(", "); String sql = String.format("INSERT INTO %s (%s) VALUES (%s)", domainModelAnalysis.getTableName(), array[0], array[1]); return sql; } /** * 将完整的SQL转换为统计SQL * 如:select a, b, c, t.d from table t * 转换后为:select count(1) from table t * * @param sql * @return */ protected String toCountSql(String sql) { if (StringUtils.isEmpty(sql)) { return null; } return sql.replaceFirst("(?<=(?i)SELECT).*?(?=(?i)FROM)", " COUNT\\(1\\) ").replaceAll("(?=(?i)order).*", ""); } /** * 添加实体 * * @param t 实体对象 * @return */ @Override public int insert(T t) { List
tableColumnList = domainModelAnalysis.getTableColumnList(); List
list = new ArrayList(); for (int i = 0, size = tableColumnList.size(); i < size; i++) { TableColumn tableColumn = tableColumnList.get(i); if (tableColumn.isTransient()) { continue; } Object value = ReflectionUtils.invokeMethod(tableColumn.getFieldGetMethod(), t); ExtendType extendType = tableColumn.getExtendType(); if (extendType != null && extendType.getCode() != ExtendType.DEFAULT.getCode()) { value = value.toString(); } list.add(value); } return jdbcTemplate.update(getInsertSql(), list.toArray(new Object[0])); } /** * 批量添加实体 * * @param list 实体对象列表 * @return */ @Override public int batchInsert(final List
list) { final List
tableColumnList = domainModelAnalysis.getTableColumnList(); final TypeHandlerRegistry typeHandlerRegistry = DomainModelContext.getTypeHandlerRegistry(); return jdbcTemplate.batchUpdate(getInsertSql(), new BatchPreparedStatementSetter() { @SuppressWarnings({ "rawtypes", "unchecked"}) @Override public void setValues(PreparedStatement ps, int i) throws SQLException { T t = list.get(i); int index = 1; for (int k = 0, size = tableColumnList.size(); k < size; k++) { TableColumn tableColumn = tableColumnList.get(k); if (tableColumn.isTransient()) { continue; } Object value = ReflectionUtils.invokeMethod(tableColumn.getFieldGetMethod(), t); TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(tableColumn.getJavaType()); typeHandler.setParameter(ps, index, value, JdbcType.NULL); index++; } } @Override public int getBatchSize() { return list.size(); } }).length; } /** * 更新实体中的非空(不含null, "")字段 * * @param t 实体对象 * @return */ @Override public int update(T t) { WhereBuilder wb = new WhereBuilder(); wb.andEquals(domainModelAnalysis.getPrimaryKey(), t.getId()); return updateByCondition(t, wb); } protected int updateByCondition(T t, WhereBuilder whereBuilder) { StringBuilder sqlBuilder = new StringBuilder(String.format("UPDATE %s SET ", domainModelAnalysis.getTableName())); List
tableColumnList = domainModelAnalysis.getTableColumnList(); Method primaryKeyMethod = null; List
values = new ArrayList(); for (int i = 0, size = tableColumnList.size(); i < size; i++) { final TableColumn tableColumn = tableColumnList.get(i); if (tableColumn.isTransient()) { continue; } if (tableColumn.isPrimaryKey()) { primaryKeyMethod = tableColumn.getFieldGetMethod(); continue; } Object value = ReflectionUtils.invokeMethod(tableColumn.getFieldGetMethod(), t); ExtendType extendType = tableColumn.getExtendType(); if (extendType != null && extendType.getCode() != ExtendType.DEFAULT.getCode()) { value = value.toString(); } if (value == null || (value instanceof String && StringUtils.isEmpty((String) value))) { continue; } values.add(value); sqlBuilder.append(tableColumn.getColumnName()) .append(" = ") .append(tableColumn.getPlaceholder()) .append(", "); } if (values.size() > 0) { int length = sqlBuilder.length(); sqlBuilder.delete(length - 2, length); String sql = sqlBuilder.toString(); sql = whereBuilder.getSql(sql); values.addAll(whereBuilder.getParameterList()); return jdbcTemplate.update(sql, values.toArray(new Object[0])); } return 0; } /** * 更新实体所有字段 * * @param t 实体对象 * @return */ @Override public int updateForce(T t) { StringBuilder sqlBuilder = new StringBuilder(String.format("UPDATE %s SET ", domainModelAnalysis.getTableName())); List
tableColumnList = domainModelAnalysis.getTableColumnList(); Method primaryKeyMethod = null; List
values = new ArrayList(); for (int i = 0, size = tableColumnList.size(); i < size; i++) { final TableColumn tableColumn = tableColumnList.get(i); if (tableColumn.isTransient()) { continue; } if (tableColumn.isPrimaryKey()) { primaryKeyMethod = tableColumn.getFieldGetMethod(); continue; } Object value = ReflectionUtils.invokeMethod(tableColumn.getFieldGetMethod(), t); values.add(value); sqlBuilder.append(tableColumn.getColumnName()) .append(" = ") .append(tableColumn.getPlaceholder()) .append(", "); } int length = sqlBuilder.length(); sqlBuilder.delete(length - 2, length); sqlBuilder.append(" WHERE ").append(domainModelAnalysis.getPrimaryKey()).append(" = ?"); values.add(ReflectionUtils.invokeMethod(primaryKeyMethod, t)); return jdbcTemplate.update(sqlBuilder.toString(), values.toArray(new Object[0])); } /** * 根据主键删除实体 * * @param id 实体主键值 * @return */ @Override public int delete(PK id) { String sql = String.format("DELETE FROM %s WHERE %s = ?", domainModelAnalysis.getTableName(), domainModelAnalysis.getPrimaryKey()); return jdbcTemplate.update(sql, new Object[]{id}); } /** * 根据主键删除实体 * * @param ids 实体主键值数组 * @return */ @Override public int delete(final PK[] ids) { String sql = String.format("DELETE FROM %s WHERE %s = ?", domainModelAnalysis.getTableName(), domainModelAnalysis.getPrimaryKey()); int[] batchUpdate = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setObject(1, ids[i]); } @Override public int getBatchSize() { return ids.length; } }); return batchUpdate.length; } /** * 查询实体列表 * * @param wb where语句拼接实例 * @return */ protected List
query(WhereBuilder wb) { String sql = String.format("SELECT %s FROM %s", domainModelAnalysis.getDefaultColumnArrayStr(), domainModelAnalysis.getTableName()); Object args[] = null; if (wb != null) { sql = wb.getSql(sql); args = wb.getParameters(); } return jdbcTemplate.query(sql, args, new DefaultRowMapper
(domainModelAnalysis)); } /** * 查询实体列表 * * @param sql select语句 * @param wb where语句拼接实例 * @return */ protected List
query(String sql, WhereBuilder wb) { Object args[] = null; if (wb != null) { sql = wb.getSql(sql); args = wb.getParameters(); } return jdbcTemplate.query(sql, args, new DefaultRowMapper
(domainModelAnalysis)); } /** * 限制返回查询记录数量 * * @param wb where语句拼接实例 * @return */ protected List
queryLimit(WhereBuilder wb) { String sql = String.format("SELECT %s FROM %s", domainModelAnalysis.getDefaultColumnArrayStr(), domainModelAnalysis.getTableName()); return queryLimit(sql, wb); } /** * 限制返回查询记录数量 * * @param querySql select语句 * @param wb where语句拼接实例 * @return */ protected List
queryLimit(String querySql, WhereBuilder wb) { Object args[] = null; int pageNum = 0, pageSize = 0; if (wb != null) { pageNum = wb.getPageNum(); pageSize = wb.getPageSize(); querySql = wb.getSql(querySql); args = wb.getParameters(); } String qsql = pageSql.getSql(querySql, pageNum, pageSize); List
list = jdbcTemplate.query(qsql, args, new DefaultRowMapper
(domainModelAnalysis)); return list; } /** * 按条件统计实体数量 * * @param wb where语句拼接实例 * @return */ protected int count(WhereBuilder wb) { String sql = String.format("SELECT COUNT(1) FROM %s", domainModelAnalysis.getTableName()); Object args[] = null; if (wb != null) { sql = wb.getSql(sql); args = wb.getParameters(); } return jdbcTemplate.queryForObject(sql, args, Integer.class); } /** * 根据主键查询实体 * * @param id 实体主键值 * @return */ @Override public T load(PK id) { String sql = String.format("SELECT %s FROM %s WHERE %s = ?", domainModelAnalysis.getDefaultColumnArrayStr(), domainModelAnalysis.getTableName(), domainModelAnalysis.getPrimaryKey()); List
list = jdbcTemplate.query(sql, new Object[]{id}, new DefaultRowMapper
(domainModelAnalysis)); return DataAccessUtils.singleResult(list); } /** * 按条件删除实体 * * @param wb where语句拼接实例 * @return */ protected int deleteByCondition(WhereBuilder wb) { String sql = String.format("DELETE FROM %s", domainModelAnalysis.getTableName()); Object args[] = null; if (wb != null) { sql = wb.getSql(sql); args = wb.getParameters(); } return jdbcTemplate.update(sql, args); } /** * 分页查询 * * @param wb where语句拼接实例 * @return */ protected Page
queryPage(WhereBuilder wb) { String sql = String.format("SELECT %s FROM %s ", domainModelAnalysis.getDefaultColumnArrayStr(), domainModelAnalysis.getTableName()); return queryPage(sql, wb); } /** * 分页查询 * * @param sql select语句 * @param wb where语句拼接实例 * @return */ protected Page
queryPage(String sql, WhereBuilder wb) { String countSql = toCountSql(sql); return queryPage(sql, countSql, wb); } /** * 分页查询 * * @param querySql select语句 * @param countSql count语句 * @param wb where语句拼接实例 * @return */ protected Page
queryPage(String querySql, String countSql, WhereBuilder wb) { Object args[] = null; int pageNum = 0, pageSize = 0; if (wb != null) { querySql = wb.getSql(querySql); args = wb.getParameters(); pageSize = wb.getPageSize(); pageNum = wb.getPageNum(); } String qsql = pageSql.getSql(querySql, pageNum, pageSize); List
list = jdbcTemplate.query(qsql, args, new DefaultRowMapper
(domainModelAnalysis)); Page
page = new Page
(); page.setData(list); if (StringUtils.isNotEmpty(countSql)) { String csql = wb.getCountSql(countSql); long count = jdbcTemplate.queryForObject(csql, args, Long.class); page.setTotal(count); } return page; } @Override public void afterPropertiesSet() throws Exception { DomainModelContext domainModelContext = SpringContextHelper.getBean(DomainModelContext.class); domainModelAnalysis = domainModelContext.registerBean(autoGetDomainClass()); }}复制代码

这里最关键的是如何解析领域模型的属性信息,我们可以在运行时通过反射把领域模型的属性信息解析出来并全局缓存起来。这步操作是在BaseDaoImpl.java的如下代码完成的---如果你不熟悉InitializingBean接口,可以搜索一下它的意义:

@Overridepublic void afterPropertiesSet() throws Exception {        DomainModelContext domainModelContext = SpringContextHelper.getBean(DomainModelContext.class);        domainModelAnalysis = domainModelContext.registerBean(autoGetDomainClass());}复制代码

下一节为大家介绍如何使用Java反射把领域模型的属性信息解析出来并全局缓存起来。

转载于:https://juejin.im/post/5a4b12f25188252b145b6887

你可能感兴趣的文章
对jquery val 获取input 文本框值进行扩展
查看>>
MySQL (select_paren) union_order_or_limit 行为
查看>>
并发不是并行,它更好!
查看>>
nltk 自己训练模型例子
查看>>
间谍卫星的基础?YOLT——利用卷积神经网络对卫星影像进行多尺度目标检测(Part I)...
查看>>
jstl_开发第一个标签
查看>>
程序员哇,你想在下个情人节或者520脱单么?这个秘籍不能错过
查看>>
去不去O,谁说了算?
查看>>
PHP防SQL注入和XSS攻击
查看>>
在SHAREPOINT共享文档库中启用版本控制功能。
查看>>
Http 代理工具 实战 支持网页与QQ代理
查看>>
又见尾递归
查看>>
安装PyGraphics
查看>>
【COCOS2DX-LUA 脚本开发之四】使用TOLUA++编译PKG,从而创建自定义类让LUA脚本使用...
查看>>
开源大数据周刊-第16期
查看>>
遥感图像分类现状及存在的问题
查看>>
Commons Logging存在的ClassLoader问题详解
查看>>
双向链表的操作
查看>>
Flume-ng 高级功能配置
查看>>
我的友情链接
查看>>