Mapper
Mapper copied to clipboard
新增通用(半通用)方法征集帖
由于有很多人都想要通用Mapper提供一些特殊的通用方法,因此在此收集一下。
通用方法可以是全数据库通用的,或者和数据库有关的方法。还可以是有使用限制的通用方法。
大家直接在这里回复即可,不要发无关的内容!
回复格式示例:
方法名:selectOneByExample
方法作用:根据 Example 查询出一个值(当结果有多个的时候抛出异常)。
数据库:全部。
用法示例[可选]:
Example example = new Example(Country.class);
example.createCriteria().andEqualTo("countryname", "China");
Country country = xxMapper.selectOneByExample(example);
SQL代码[可选]:
select * from country where countryname = 'China‘
其他补充:xxx
如果能提供方法对应的 XML 代码更好!
【说明】:该方法已经实现
第一个~
方法名:insertList
方法作用:批量插入
数据库:部分支持批量插入的数据库(insert values(),())
用法示例[可选]:
List<A> aList = new ArrayList<A>();
A a = new A();
//不支持回显id,必须指定
a.setId(xx);
aList.add(a);
xxMapper.insertList(aList);
SQL代码[可选]:
insert into country (id, name) values(?,?),(?,?)
【说明】:该方法已经实现
方法名:updateSelectiveWithNull (T t, List<String> nullFields)/ insertSelectiveWithNull(T t, List<String> nullFields) 方法作用:根据 T类 修改/新增对象(nullFields中的字段名处理方式为修改空/插入空), 数据库:全部。 用法示例: update table t set t.id='1', name=null where code='China'
方法名:updateByPrimaryKeyWithVersion 方法作用:根据主键+乐观锁进行更新 数据库:MySQL(一直在用MySQL)。
方法名:selectByExampleselectByExample(@Param("example") TLicenseToolsCriteria example); 方法作用:插入或者查询where增加每个参数的list类型,增加根据某一个字段排序。 数据库:全部。 用法示例: List list=new ArrayList(); list.add(1); TLawMenuCriteria lawMenuCriteria = new TLawMenuCriteria(); lawMenuCriteria.setIdList(list); lawMenuCriteria.setOrderByClause("id");
方法名:selectBetweenDate(Date startDate,Date endDate) 方法作用:根据startDate endDate查询有效的(历史)数据 数据库:mysql。 SQL代码[可选]: select * from country where country.start_time <= #{startDate} AND (country.end_time >= now() OR country.end_time IS NULL ) 其他补充:目前我用example可以实现: example.createCriteria() .andLessThanOrEqualTo("startDate",date) .andEqualTo("employeeId",employeeId); example.and().andIsNull("endDate").orGreaterThanOrEqualTo("endDate",date);
方法名:insertListSelective 方法作用:批量插入 数据库:部分支持批量插入的数据库(insert values(),()) 用法示例[可选]:
方法名:updateListSelective
方法作用:批量更新
数据库:部分支持批量更新的数据库(update values(),())
用法示例[可选]:
xml 语句
方法名:selectListDataScope 方法作用:根据范围插件结果集,范围为非ID字段. 数据库:全部数据库 用法示例:
List list=new ArrayList();
list.add(1);
list.add(2);
list.add(3);
xxMapper.selectListDataScope("dept_id",list);
SQL代码:
select * from temp_data_scope where temp_data_scope.dept_id in (?,?,? )
方法名:selectByExampleWithoutBlob 方法作用:根据 Example 查询出非大字段的数据。 数据库:全部。
方法名:
/**
* 根据实体接口类型,选择物理删除还是逻辑删除
*
* @param record
* @return
*/
@UpdateProvider(type = BaseDeleteProvider.class, method = "dynamicSQL")
int deleteIfLogicByPrimaryKey(T record);
方法作用:根据实体接口类型,选择物理删除还是逻辑删除 数据库:全部。 实现方法:
/**
* TODO Carl.Jia 2018-01-05 19:55:26 可以优化,问题是逻辑删除后实体 del_flag 没有回写
* 选择物理删除还是逻辑删除
*
* @param ms
* @return
*/
public String deleteIfLogicByPrimaryKey(MappedStatement ms) {
Class<?> entityClass = getEntityClass(ms);
StringBuilder sql = new StringBuilder();
String tableName = tableName(entityClass);
if (LogicDeleteEntity.class.isAssignableFrom(entityClass)) {
sql.append(SqlHelper.updateTable(entityClass, tableName));
sql.append("<set>");
sql.append(LogicDeleteEntity.DEL_FIELD_NAME).append("=").append(LogicDeleteEntity.DEL).append(",");
//获取全部列
Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
// 如果是审计子类,需要更新审计字段
if (AuditDataEntity.class.isAssignableFrom(entityClass)) {
for (EntityColumn column : columnList) {
if ("updateBy".equals(column.getProperty()) || "updateDate".equals(column.getProperty())) {
sql.append(column.getColumnEqualsHolder(null)).append(",");
}
}
}
sql.append("</set>");
sql.append(SqlHelper.wherePKColumns(entityClass));
}else{
sql.append(SqlHelper.deleteFromTable(entityClass, tableName));
sql.append(SqlHelper.wherePKColumns(entityClass));
}
return sql.toString();
}
public interface LogicDeleteEntity {
String DEL = "1";
/**
* 实体类执行逻辑删除操作时的标记操作.
*/
String DEL_FIELD_NAME = "del_flag";
}
@CarlJia 这个可以有,逻辑删除的列可以参考我刚新增的 @Version
注解,加注解比接口要方便。
能不能直接PR一个?
@abel533 这个只是我粗略的加的,有好多没完善的,等我稍晚会提个 PR 上去吧,我去看看你加的 @Version
那个
@abel533 已提交 PR 不过没有加文档,如果功能 ok 我可以补上说明
注解名:LIkeColumn.class 作用:添加该注解的字段进行select时将使用模糊查询,而不是=号查询 数据库:全部
我业务里面经常需要进行对特定字段进行模糊查询 其他字段进行等号查询 所以有这样一个注解类会比较方便,我自己已经改了下源码,添加了一个注解类了
@biggirlo 听起来比较合理。。可以考虑一下除了模糊还有什么其他情况。。可以增加一个 @SelectOptions注解,对select查询增加更多的配置。
方法名:insertListBatch(好像最新版本已经增加了这个方法了,新增了就忽略 :) ) 作用:批量插入,不需要主键,自己塞PrimaryKey进去,比如uuid 代码: /** * 批量插入,不需要主键,自己塞PrimaryKey进去,比如uuid * * @param recordList * @return */ @InsertProvider(type = SpecialProvider.class, method = "dynamicSQL") int insertListBatch(List<T> recordList);
参考实现 /** * 批量插入 * * @param ms */ public String insertListBatch(MappedStatement ms) { final Class<?> entityClass = getEntityClass(ms); //开始拼sql StringBuilder sql = new StringBuilder(); sql.append(SqlHelper.insertIntoTable(entityClass, tableName(entityClass))); sql.append(SqlHelper.insertColumns(entityClass, false, false, false)); sql.append(" VALUES "); sql.append("<foreach collection="list" item="record" separator="," >"); sql.append("<trim prefix="(" suffix=")" suffixOverrides=",">"); //获取全部列 Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass); //当某个列有主键策略时,不需要考虑他的属性是否为空,因为如果为空,一定会根据主键策略给他生成一个值 for (EntityColumn column : columnList) { if (column.isInsertable()) { sql.append(column.getColumnHolder("record") + ","); } } sql.append(""); sql.append(""); return sql.toString(); }
@cocoa2003 新版本的这个方法就是这样,不过名字仍然是 insertList,不能和另一个insertList同时存在。
方法名:selectAggregationByExample
方法作用:根据 Example 和聚合参数查询聚合。
数据库:全部。
参考实现:
@waitQuietly 我觉得这个方法可以,虽然用的 ${}
可能注入,但是这里传进来的参数应该都是后台定义好的,不存在前台传入字段的情况。
能不能 fork 项目在 extra 模块的这个目录(extra/src/main/java/tk/mybatis/mapper/additional/aggregation
)增加你这个方法,然后 PR?
GroupBy 在某种意义上可以参考 Example 中的 OrderBy 封装一个对象,在 $ 中 toString().
@abel533 行, 还没用过mapper4, 需要集成测试下
方法名:updateByConditionMap 方法作用:根据Condition条件更新某些字段值 数据库:全部。 代码: @RegisterMapper public interface UpdateWithMapMapper<T> {
@UpdateProvider(type = UpdateWithMapSpecialProvider.class, method = "dynamicSQL")
int updateByConditionMap(@Param("params") Map<String,Object> params, @Param("example") Condition condition);
}
public class UpdateWithMapSpecialProvider extends MapperTemplate {
public UpdateWithMapSpecialProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public String updateByConditionMap(MappedStatement ms) {
final Class<?> entityClass = getEntityClass(ms);
StringBuilder sql = new StringBuilder();
// 安全更新,Example 必须包含条件
if (getConfig().isSafeUpdate()) {
sql.append(SqlHelper.exampleHasAtLeastOneCriteriaCheck("example"));
}
sql.append(SqlHelper.updateTable(entityClass, tableName(entityClass), "example"));
sql.append("<set>");
// sql.append("<foreach collection="params.keys" item="key" open="(" close=")" separator="," >"); sql.append("<foreach collection="params.keys" item="key" separator="," >"); sql.append("${key}"); sql.append(" = "); sql.append(" #{params[${key}]} "); sql.append(""); sql.append(""); sql.append(SqlHelper.updateByExampleWhereClause()); return sql.toString(); }
}
方法名:deleteList 方法作用:根据批量删除 数据库:全部。 代码: `@RegisterMapper public interface DeleteListMapper<T> {
/**
*
* @param recordList 建议个数在1000之内
* @return
*/
@DeleteProvider(type = DeleteSpecialProvider.class, method = "dynamicSQL")
int deleteList(List<T> recordList);
}
public class DeleteSpecialProvider extends MapperTemplate{
public DeleteSpecialProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public String deleteList(MappedStatement ms) {
final Class<?> entityClass = getEntityClass(ms);
//开始拼SQL
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.deleteFromTable(entityClass, tableName(entityClass)));
sql.append("<where>");
sql.append("<foreach collection=\"list\" item=\"record\" separator=\" or \" >");
sql.append("<trim prefix=\"(\" suffix=\")\" prefixOverrides=\"AND |OR \" >");
//获取全部列
Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
List<String> whereListSql = new ArrayList<>();
StringBuilder whereSql = new StringBuilder();
columnList.forEach(o->{
String record = " AND " + o.getColumnEqualsHolder("record");
String ifNotNull = SqlHelper.getIfNotNull("record",o, record, true);
whereListSql.add(ifNotNull);
});
// whereSql.append(StringUtils.join(whereListSql, " AND ")); whereSql.append(StringUtils.join(whereListSql, "")); sql.append(whereSql); sql.append(""); sql.append(""); sql.append(""); return sql.toString(); } }`
有没有可以指定返回类型的通用select系列方法,诸如selectOne,selectByExample,selectByPrimaryKey等等凡是select方法都能用的。每次都用stream来转换类型太麻烦了。 比如现在我的mapper对应类A,但是我有个相仿的只包括了A中部分字段的类B,然后我现在希望只查询B中的字段,要么只能自己写sql单独匹配,要么只能用Amapper.selectAll().stream.map(xxxxxx).collect(Collectors.toList())这样的办法,而且后者每次查询会查出所有数据,查询效率低,stream.map也有不小的开销。 可以给这些方法一个重载参数Class<T>resultType,或者取个selectOneFor,selectByExampleFor之类的名字,返回值类型直接就变成T了。
有没有可以指定返回类型的通用select系列方法,诸如selectOne,selectByExample,selectByPrimaryKey等等凡是select方法都能用的。每次都用stream来转换类型太麻烦了。 比如现在我的mapper对应类A,但是我有个相仿的只包括了A中部分字段的类B,然后我现在希望只查询B中的字段,要么只能自己写sql单独匹配,要么只能用Amapper.selectAll().stream.map(xxxxxx).collect(Collectors.toList())这样的办法,而且后者每次查询会查出所有数据,查询效率低,stream.map也有不小的开销。 可以给这些方法一个重载参数ClassresultType,或者取个selectOneFor,selectByExampleFor之类的名字,返回值类型直接就变成T了。
Amapper.selectAll().stream.map(xxxxxx).collect(Collectors.toList())
每次这样写很抓狂
方法名:insertOrUpdateByDuplicateKey 方法作用:新增或根据唯一索引更新。 数据库:MySQL。 用法示例:
Dict record1 = new Dict().setCode("test").setValue("test value");
dictMapper.insertOrUpdate(record1);
SQL代码:
INSERT INTO `dict` ( `id`,`code`,`value` ) VALUES( ?,?,? ) ON DUPLICATE KEY UPDATE `code` = values(`code`),`value` = values(`value`) ;
实现示例:
// 在原insert/insertSelective/insertList生成的SQL后加上下面的SQL
/**
* @param sql
* @param columnList
* @param selective 只修改不为空的值
* @return
*/
private void appendUpdateSql(StringBuilder sql, Set<EntityColumn> columnList, boolean selective) {
sql.append("ON DUPLICATE KEY UPDATE ");
sql.append("<trim suffixOverrides=\",\">");
for (EntityColumn column : columnList) {
if (!column.isUpdatable()) {
continue;
}
if (column.isIdentity()) {
continue;
}
String s = "" + column.getColumn() + " = values(" + column.getColumn() + "),";
if (selective) {
sql.append(SqlHelper.getIfNotNull(column, s, isNotEmpty()));
} else {
sql.append(s);
}
}
sql.append("</trim>");
}
有没有批量更新的方法?