Mapper icon indicating copy to clipboard operation
Mapper copied to clipboard

新增通用(半通用)方法征集帖

Open abel533 opened this issue 6 years ago • 26 comments

由于有很多人都想要通用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 代码更好!

【说明】:该方法已经实现

abel533 avatar Dec 25 '17 06:12 abel533

第一个~

FlyingPigHasDream avatar Dec 25 '17 06:12 FlyingPigHasDream

方法名: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(?,?),(?,?)

【说明】:该方法已经实现

abel533 avatar Dec 25 '17 06:12 abel533

方法名: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'

asiamaster avatar Dec 25 '17 06:12 asiamaster

方法名:updateByPrimaryKeyWithVersion 方法作用:根据主键+乐观锁进行更新 数据库:MySQL(一直在用MySQL)。

gsfeng179 avatar Dec 25 '17 06:12 gsfeng179

方法名:selectByExampleselectByExample(@Param("example") TLicenseToolsCriteria example); 方法作用:插入或者查询where增加每个参数的list类型,增加根据某一个字段排序。 数据库:全部。 用法示例: List list=new ArrayList(); list.add(1); TLawMenuCriteria lawMenuCriteria = new TLawMenuCriteria(); lawMenuCriteria.setIdList(list); lawMenuCriteria.setOrderByClause("id");

wenzheleader avatar Dec 25 '17 06:12 wenzheleader

方法名: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);

wupy1108 avatar Dec 25 '17 07:12 wupy1108

方法名:insertListSelective 方法作用:批量插入 数据库:部分支持批量插入的数据库(insert values(),()) 用法示例[可选]:

方法名:updateListSelective 方法作用:批量更新 数据库:部分支持批量更新的数据库(update values(),()) 用法示例[可选]: xml 语句 image

leeyuanhe avatar Dec 27 '17 09:12 leeyuanhe

方法名: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 (?,?,? )

cuisongliu avatar Jan 03 '18 03:01 cuisongliu

方法名:selectByExampleWithoutBlob 方法作用:根据 Example 查询出非大字段的数据。 数据库:全部。

littlefisher666 avatar Jan 04 '18 03:01 littlefisher666

方法名:

  /**
     * 根据实体接口类型,选择物理删除还是逻辑删除
     *
     * @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 avatar Jan 08 '18 02:01 CarlJia

@CarlJia 这个可以有,逻辑删除的列可以参考我刚新增的 @Version 注解,加注解比接口要方便。

能不能直接PR一个?

abel533 avatar Jan 08 '18 05:01 abel533

@abel533 这个只是我粗略的加的,有好多没完善的,等我稍晚会提个 PR 上去吧,我去看看你加的 @Version 那个

CarlJia avatar Jan 08 '18 06:01 CarlJia

@abel533 已提交 PR 不过没有加文档,如果功能 ok 我可以补上说明

CarlJia avatar Jan 08 '18 08:01 CarlJia

注解名:LIkeColumn.class 作用:添加该注解的字段进行select时将使用模糊查询,而不是=号查询 数据库:全部

我业务里面经常需要进行对特定字段进行模糊查询 其他字段进行等号查询 所以有这样一个注解类会比较方便,我自己已经改了下源码,添加了一个注解类了

biggirlo avatar Jan 09 '18 17:01 biggirlo

@biggirlo 听起来比较合理。。可以考虑一下除了模糊还有什么其他情况。。可以增加一个 @SelectOptions注解,对select查询增加更多的配置。

abel533 avatar Jan 10 '18 14:01 abel533

方法名: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 avatar Jan 12 '18 05:01 cocoa2003

@cocoa2003 新版本的这个方法就是这样,不过名字仍然是 insertList,不能和另一个insertList同时存在。

abel533 avatar Jan 13 '18 13:01 abel533

方法名:selectAggregationByExample 方法作用:根据 Example 和聚合参数查询聚合。 数据库:全部。 参考实现: image image

wittyliu avatar Mar 21 '18 08:03 wittyliu

@waitQuietly 我觉得这个方法可以,虽然用的 ${} 可能注入,但是这里传进来的参数应该都是后台定义好的,不存在前台传入字段的情况。

能不能 fork 项目在 extra 模块的这个目录(extra/src/main/java/tk/mybatis/mapper/additional/aggregation)增加你这个方法,然后 PR?

GroupBy 在某种意义上可以参考 Example 中的 OrderBy 封装一个对象,在 $ 中 toString().

abel533 avatar Mar 24 '18 09:03 abel533

@abel533 行, 还没用过mapper4, 需要集成测试下

wittyliu avatar Mar 25 '18 05:03 wittyliu

方法名: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(); }

}

WenTao-Love avatar Jul 12 '18 01:07 WenTao-Love

方法名: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(); } }`

WenTao-Love avatar Jul 12 '18 01:07 WenTao-Love

有没有可以指定返回类型的通用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了。

Frodez avatar Mar 15 '19 14:03 Frodez

有没有可以指定返回类型的通用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()) 每次这样写很抓狂

youlingme avatar Aug 28 '19 03:08 youlingme

方法名: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>");
    }

u1zz avatar Aug 18 '20 06:08 u1zz

有没有批量更新的方法?

fenfeinuli avatar Sep 30 '20 03:09 fenfeinuli