JSeckill由Only丿阿海业余时间封装,是一个基于SSM技术且适用于极速开发的J2EE框架,且看以下优点:
Mybatis模块(全名: EasyMybatis)顾名思义, 是在Mybatis的基础上进行了简单的封装, 详见https://easymybatis.com/
后续会增加更多的模块, 组成JSeckill框架.
(汗颜… 因为时间和精力问题, 这篇文章自创建起近三年没有更新, 后期内容会慢慢增加.)
使用EasyMybatis, 可以省去你编写mapper文件的烦恼. 甚至省去你自动生成代码的操作! 你无需再因为数据库中某个字段的改动而去修改编大量的xml, 甚至你无需生成死板的sql模板和mapper文件, EasyMybatis在每次程序启动时会根据你的实体类在内存中直接生成你需要的SQL, 和Mapper. 他不会释放文件到硬盘, 也不会生成繁琐的代码, 使你的项目看起来更清爽.
在你的项目依赖中直接引入如下依赖即可
<!-- EasyBatis集成 -->
<dependency>
<groupId>com.easymybatis.freamwork</groupId>
<artifactId>spring-easymybatis-core</artifactId>
<version>0.0.4.RELEASE</version>
</dependency>
mvn clean install
以安装到你的仓库中为保证实体类多项目的通用性, 不要将新的实体类定义到你应用的项目中.
应抽离出来, 定义在一个独立的项目中, 以此来供给多个项目引用使用.
主要解决以下问题:
一般情况下, 使用VO即可, 在复杂场景中, 可以PO与VO分开来使用. 在一些特殊场景中, (例如统计图等相关接口中) 需要给客户端传利高度封装的定制对象时使用DTO.
/**
* @author gsk
* `@TableName`注解中的name值 标识该实体类对应数据库中的表名
* 继承PO类, 则表示这个实体类可以对数据库中对应的表进行操作
*/
@TableName(name = "persion_info")
public class PersionPO extends PO{
/**
* 对应数据库的主键字段(该注解支持字段的类型有: int, long, String)
*/
@ID
private Long id;
/**
* `@FieldName`注解中的name值 标识该字段对应数据库表中的指定字段名
*/
@FieldName(name = "persion_name")
private String name;
/**
* 不使用@FieldName 注解时, 系统会使用当前字段名作为数据库表中的字段名进行操作
* (会转换为下划线命名方式)
* 例如该字段, 系统会默认认为它对应数据库的字段名为`age_num`
*/
private String ageNum;
/**
* 该`@TempField`注解标识这个字段不参与数据库操作.
*/
@TempField
private int nicker;
// getter .... setter
}
简单而言, 数据库操作类直接继承BaseDaoImpl即可实现几乎所有的数据库操作,
尽量避免使用自动生成Mapper, 避免日后实体类有改动时, Mapper要同步修改,
避免实体类和mapper不同步问题. 代码如下:
/**
* @author gsk
*/
@Repository
public class PersionDao extends BaseDaoImpl<PersionPO, Long> {
// 此处无需写过多的代码. BaseDaoImpl本身实现了公共的数据库操作.
}
一个简单的代码实例, 无需写SQL, 简单的一行代码就实现了多条件, 分页, 排序等功能:
/**
* @author gsk
*/
@Service
public class PersionServiceImpl implements PersionService {
@Resource
private PersionDao persionDao;
/**
* 列出年轻的小姐姐列表, 并案年龄倒序排序
* @param pageNum
* 页码
* @param length
* 每页长度
* @return
*/
public List<PersionPO> listYoungGirl(int pageNum, int length){
long startNum = (pageNum - 1) * length;
// 18岁及18岁以下的定义为年轻, sex = 0 定义为女
return persionDao.list(Method.where("age_num", C.XE, 18).and("sex", C.EQ, 0).limit(startNum, length).orderBy("age_num desc"));
}
}
DAO中, 大部分的操作会用到Method表达式, Method表达式为链式操作, 且不用在意顺序.
可以根据业务判断和流程在不同的场景中动态构建你所需的SQL WHERE 表达式
, 它表达了我们要查询的数据库的目标.
主要分为[字段名, 比较符, 比较值] 三部分.
示例(目标为: 18岁的人群中的小姐姐按照年龄倒序的前十条):
// 18岁的人群
WherePrams where = Method.where("age_num", C.EQ, 18);
// 中的小姐姐
where.and("sex", C.EQ, 0);
// 按照年龄倒序
where.orderBy("age_num desc");
// 的前十条
where.limit(0, 10);
当不确定首要条件的时候, 可以用createDefault()方法创建一个无目标的表达式.
示例(当传入年龄 <= 18时, 取出10个18岁小姐姐, 否则取出20个指定年龄的小哥哥)
public List<PersionPO> list(int age){
WherePrams where = Method.createDefault();
if(age <= 18){
where.and("age_num", C.EQ, 18).and("sex", C.EQ, 0).limit(0, 10);
}else{
where.and("age_num", C.EQ, age).and("sex", C.EQ, 1).limit(0, 20);
}
return persionDao.list(where);
}
直接在DAO实现类中添加自定义SQL方法即可, 假设每个人都有一个朋友,
并且他们是自关联的表, 这个时候要查询出所有小哥哥的女朋友, 并且分页展示,
这个时候需要执行一个比较复杂的SQL, 如下:
SELECT res.* FROM persion_info WHERE res.id IN (
SELECT friendId FROM persion_info WHERE sex = 1
) res LIMIT 0, 10;
只需建立这个SQL的实现方法即可.
/**
* @author gsk
*/
@Repository
public class PersionDao extends BaseDaoImpl<PersionPO, Long> {
/**
* 出所有小哥哥的女朋友, 并且分页展示
* @param pageNum
* 页码
* @param length
* 每页条数
* @return
*/
List<PersionPO> listPersionFriend(int pageNum, int length){
int start = (pageNum - 1) * length;
String sql = "SELECT res.* FROM WHERE res.id IN (" +
" SELECT friendId FROM persion_info WHERE sex = ?" +
") res LIMIT ?, ?;";
return list(sql, 1, start, length);
}
}
在以上代码中, 要查询的字段是 res.*, 这个过程其实比较耗时, 我需要更规范的写法: SELECT res.id, res.name, res.xxx ... FROM
这类的.
再比如SELECT friendId FROM persion_info
中的persion_info
这个表名, 也是写死的, 假设表名变更或者字段变更的话,
则需要修改业务代码, 这个时候使用内部函数来动态得到要查询的字段和表名, 便可解决后期的SQL维护问题.
如下:
List<PersionPO> listPersionFriend(int pageNum, int length){
int start = (pageNum - 1) * length;
// 动态生成SQL, 填充字段列表和表名
String sql = "SELECT %s FROM WHERE id IN (" +
" SELECT friendId FROM %s WHERE sex = ?" +
") LIMIT ?, ?;";
sql = String.format(sql, selectSql(), tableName());
// 执行SQL
return list(sql, 1, start, length);
}
继续以以上SQL为例, 将以上Sql使用Mapper模板来执行:
1,建立Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.front.pay.dao.PersionDao">
<select id="listPersionFriend" parameterType="hashmap" resultMap="com.front.entity.PersionPO">
SELECT ${selectSql} FROM WHERE id IN(
SELECT friendId FROM ${tableName} WHERE sex = #{sex}
) res LIMIT #{start}, #{length};
</select>
</mapper>
2,添加DAO方法映射
/**
* @author gsk
*/
@Repository
public class PersionDao extends BaseDaoImpl<PersionPO, Long> {
@Resource
private SqlSessionTemplate sqlSessionTemplate;
/**
* 出所有小哥哥的女朋友, 并且分页展示
* @param pageNum
* 页码
* @param length
* 每页条数
* @return
*/
List<PersionPO> listPersionFriend(int pageNum, int length){
Map<String, Object> params = new HashMap<>();
params.put("start", (pageNum - 1) * length);
params.put("length", length);
params.put("selectSql", selectSql());
params.put("tableName", tableName());
return sqlSessionTemplate.selectList("listPersionFriend", params);
}
}
其实以上语句是可以使用强大的Method表达式动态生成的, 善用Script
对象, 它可以生成你想要的SQL脚本而不执行它:
Method.where("id", C.IN,
Script.of("SELECT parentId FROM ", tableName(), " ", Method.where("sex", 1))
).limit(1, 10);
待续…