使用 Provider 注解实现动态 SQL
Mybatis3 中增加了使用注解来配置 Mapper 的新特性,这里主要介绍 @SelectProvider、@UpdateProvider、@InsertProvider 和 @DeleteProvider 的使用方式。
这几个注解声明在 Mapper 对应的 interface 的方法上的,注解用于生成查询用的 sql 语句。如果对应的 Mapper 中已使用 @Param 来注解参数,则在对应的 Prodiver 的方法中无需写参数。
注解中参数:
- type:参数指定的 Class 类,必须要能够通过无参的构造函数来初始化;
- method:参数指定的方法,必须是 public 的,返回值必须为 String,可以为 static。
一、@SelectProvider
@ResultMap 注解用于从查询结果集 ResultSet 中取数据然后拼装实体 bean。
public interface UserMapper {
@SelectProvider(type = SqlProvider.class, method = "selectUser")
@ResultMap("userMap")
public User getUser(long userId);
}
public class SqlProvider {
public String selectUser(long userId){
SELECT("id, name, email");
FROM("USER");
WHERE("ID = #{userId}");
}
}
上例中定义了一个 Mapper 接口,其中定义了一个 getUser 方法,这个方法根据用户 id 来获取用户信息,并返回相应的 User。而对应的 SQL 语句则写在 SqlProvider 类中。
二、@InsertProvider
public interface UserMapper {
@InsertProvider(type = SqlProvider.class, method = "addUser")
@Options(useGeneratedKeys = true, keyProperty = "id")
int addUser(Tutor tutor);
}
public class SqlProvider {
public String addUser(User user) {
return new SQL() {
{
INSERT_INTO("USER");
if (user.getName() != null) {
VALUES("NAME", "#{name}");
}
if (user.getEmail() != null) {
VALUES("EMAIL", "#{email}");
}
}
}.toString();
}
}
三、@UpdateProvider
public interface UserMapper {
@UpdateProvider(type = SqlProvider.class, method = "updateUser")
int updateUser(User user);
}
public class SqlProvider {
public String updateUser(User user) {
return new SQL() {
{
UPDATE("USER");
if (user.getName() != null) {
SET("NAME = #{name}");
}
if (user.getEmail() != null) {
SET("EMAIL = #{email}");
}
WHERE("ID= #{id}");
}
}.toString();
}
}
四、@DeleteProvider
public interface UserMapper {
@DeleteProvider(type = SqlProvider.class, method = "deleteUser")
int deleteUser(int id);
}
public class SqlProvider {
public String deleteUser(int id) {
return new SQL() {
{
DELETE_FROM("USER");
WHERE("ID= #{id}");
}
}.toString();
}
}
项目实践
在一次项目中,我需要导入一个 excel 文件,该文件每一行有两百多个字段,我需要根据数据库中保存的字段导入规则进行校验,校验成功后,我需要将存在的字段插入数据库中,如果两百多个字段我都在 Mapper 文件使用 <if>
动态标签的话,那不仅费时费力,还极难维护,所以我们就可以通过 @InsertProvider 标签,根据数据库中查询出来的字段规则,动态生成 SQL 语句:
import cn.uni.app.entity.csv.ColumnInfo;
import cn.uni.app.utils.ValidUtil;
import org.apache.ibatis.jdbc.SQL;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Map;
public class HospMainSql {
public String addJxkhMain(List<ColumnInfo> list, Map<String, Object> params, String isZy) {
return new SQL() {
{
//isZy=0 则将数据插入 t_card_info_import 中,否则插入 t_card_info_import_zy
if ("0".equals(isZy)) {
INSERT_INTO("t_card_info_import");
} else {
INSERT_INTO("t_card_info_import_zy");
}
//遍历所有字段规则,如果需要导入的数据中包含该字段,则将该字段插入数据库
for (ColumnInfo info : list) {
Boolean bool = params.containsKey(info.getColname());
if (bool) {
switch (info.getColtype()) {
//如果是字符串类型
case "C":
VALUES(info.getColname(), "'" + params.get(info.getColname()).toString() + "'");
break;
//如果是日期类型
case "D":
VALUES(info.getColname(), "'" + params.get(info.getColname()).toString() + "'");
break;
//如果是数值类型
case "N":
if (!StringUtils.isEmpty(params.get(info.getColname()).toString().trim())) {
VALUES(info.getColname(), ValidUtil.isNumeric1(params.get(info.getColname()).toString()));
}
break;
}
}
}
/**
* 下面几个字段与业务无关,在 ts_upload_column_info 中无约束
*/
VALUES("hospcode", "'" + params.get("hospcode").toString() + "'");
VALUES("hisid", "'" + params.get("hisid").toString() + "'");
//调用数据库中编写的,newId() 函数,生成 suid
VALUES("suid", "newid()");
//判断是否存在 UploadId
Boolean tag = params.containsKey("UploadId");
if (tag) {
VALUES("UploadId", "'" + params.get("UploadId").toString() + "'");
}
}
}.toString();
}
}
public class ColumnInfo {
private Integer colid;
private String colname;//字段名
private String coldesc;//字段描述
private String coltype;//数据类型
private Integer collen;//数据长度
private Integer colprec;//数据精度(如果是 decimal)
private Character notnull;//是否能为空
private Integer orderid;//排序权重
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: MyBatis 动态 SQL 标签
下一篇: 谈谈自己对于 AOP 的了解
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论