总所周知,tkmybatis是基于 Mybatis 框架开发的一个工具,内部实现了对单表的基本数据操作,只需要简单继承 TK mybatis 提供的接口,就能够实现无需编写任何 sql 即能完成单表操作。因此,使用TK mybatis通常只需要
1、引包
2、扫描包
3、继承Mapper
public interface TestMapper extends BaseMapper<TestData>{
}
完成这一套动作,按理来说就可以正常使用了,增改查都没问题了,但是,删除不行!
为什么删除不行呢?
我想到的第一步是检查逻辑删除标识
利用TK mybatis 逆向工程生成数据库表的Mapper、Model和对应的XML文件之后,我们就可以正常使用增删改查功能了。在model中,针对于每一个数据表中都有相应的注解标识字段和表含义:@Table:数据表信息
@Column:数据表的列信息
@LogicDelete:逻辑删除字段标识
检查完代码之后,我发现我的代码中所有对应的删除标识都标记了逻辑删除字段的,按理来说是可以正常删除的。我将对应的sql语句放在navicat中执行是成功的,这说明我使用的方法是正确的,所以是不是TKmybatis本身就不支持删除方法呢,为了搞清楚这个原因,我对于TKmybatis的代码进行了深入的研究。
在tkmybatis的baseMapper中,它继承了增删改查四种类型的mapper
@tk.mybatis.mapper.annotation.RegisterMapper
public interface BaseMapper<T> extends
BaseSelectMapper<T>,
BaseInsertMapper<T>,
BaseUpdateMapper<T>,
BaseDeleteMapper<T> {
}
其中增删改查都继承了多种方式
//查询
@RegisterMapper
public interface BaseSelectMapper<T> extends
SelectOneMapper<T>,
SelectMapper<T>,
SelectAllMapper<T>,
SelectCountMapper<T>,
SelectByPrimaryKeyMapper<T>,
ExistsWithPrimaryKeyMapper<T> {
}
//更新
@RegisterMapper
public interface BaseUpdateMapper<T> extends
UpdateByPrimaryKeyMapper<T>,
UpdateByPrimaryKeySelectiveMapper<T> {
}
//插入
@RegisterMapper
public interface BaseInsertMapper<T> extends
InsertMapper<T>,
InsertSelectiveMapper<T> {
}
//删除
@RegisterMapper
public interface BaseDeleteMapper<T> extends
DeleteMapper<T>,
DeleteByPrimaryKeyMapper<T> {
}
但是删除语句中,继承的分别是DeleteMapper<T>、DeleteByPrimaryKeyMapper<T>两种删除方式,在我们使用删除时,使用了自己在baseMapper中定义的四种删除方法
/**
* 根据实体属性作为条件进行删除,查询条件使用等号
*
* @param record
* @return
*/
int deleteSoft(T record);
/**
* 根据主键字段进行删除,方法参数必须包含完整的主键属性
*
* @param key
* @return
*/
int deleteSoftByPrimaryKey(Object key);
/**
* 根据Example条件删除数据
*
* @param example
* @return
*/
int deleteSoftByExample(Object example);
/**
* 根据主键字符串进行删除,类中只有存在一个带有@Id注解的字段
*
* @param ids 如 "1,2,3,4"
* @return
*/
int deleteSoftByIds(String ids);
而在tkmybatis中BaseDeleteProvider仅有两种删除方法的实现
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2017 abel533@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package tk.mybatis.mapper.provider.base;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import tk.mybatis.mapper.mapperhelper.EntityHelper;
import tk.mybatis.mapper.mapperhelper.MapperHelper;
import tk.mybatis.mapper.mapperhelper.MapperTemplate;
import tk.mybatis.mapper.mapperhelper.SqlHelper;
import tk.mybatis.mapper.util.MetaObjectUtil;
/**
* BaseDeleteMapper实现类,基础方法实现类
*
* @author liuzh
*/
public class BaseDeleteProvider extends MapperTemplate {
public BaseDeleteProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
/**
* 通过条件删除
*
* @param ms
* @return
*/
public String delete(MappedStatement ms) {
Class<?> entityClass = getEntityClass(ms);
StringBuilder sql = new StringBuilder();
//如果设置了安全删除,就不允许执行不带查询条件的 delete 方法
if (getConfig().isSafeDelete()) {
sql.append(SqlHelper.notAllNullParameterCheck("_parameter", EntityHelper.getColumns(entityClass)));
}
// 如果是逻辑删除,则修改为更新表,修改逻辑删除字段的值
if (SqlHelper.hasLogicDeleteColumn(entityClass)) {
sql.append(SqlHelper.updateTable(entityClass, tableName(entityClass)));
sql.append("<set>");
sql.append(SqlHelper.logicDeleteColumnEqualsValue(entityClass, true));
sql.append("</set>");
MetaObjectUtil.forObject(ms).setValue("sqlCommandType", SqlCommandType.UPDATE);
} else {
sql.append(SqlHelper.deleteFromTable(entityClass, tableName(entityClass)));
}
sql.append(SqlHelper.whereAllIfColumns(entityClass, isNotEmpty()));
return sql.toString();
}
/**
* 通过主键删除
*
* @param ms
*/
public String deleteByPrimaryKey(MappedStatement ms) {
final Class<?> entityClass = getEntityClass(ms);
StringBuilder sql = new StringBuilder();
if (SqlHelper.hasLogicDeleteColumn(entityClass)) {
sql.append(SqlHelper.updateTable(entityClass, tableName(entityClass)));
sql.append("<set>");
sql.append(SqlHelper.logicDeleteColumnEqualsValue(entityClass, true));
sql.append("</set>");
MetaObjectUtil.forObject(ms).setValue("sqlCommandType", SqlCommandType.UPDATE);
} else {
sql.append(SqlHelper.deleteFromTable(entityClass, tableName(entityClass)));
}
sql.append(SqlHelper.wherePKColumns(entityClass));
return sql.toString();
}
}
因此,当我们使用除了delete、deleteByPrimaryKey的逻辑删除方法时,需要自己重新定义一种DeleteProvider,保证自身所调用的逻辑软删是有效的。
@DeleteProvider(type = MyBaseDeleteProvider.class, method = "dynamicSQL")
int deleteSoft(T record);
@DeleteProvider(type = MyBaseDeleteProvider.class, method = "dynamicSQL")
int deleteSoftByPrimaryKey(Object key);
@DeleteProvider(type = MyBaseDeleteProvider.class, method = "dynamicSQL")
int deleteSoftByExample(Object example);
@DeleteProvider(type = MyBaseDeleteProvider.class, method = "dynamicSQL")
int deleteSoftByIds(String ids);
public class MyBaseDeleteProvider extends MapperTemplate {
public MyBaseDeleteProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
/**
* 通过条件删除
*
* @param ms
* @return
*/
public String deleteSoft(MappedStatement ms) {
Class<?> entityClass = getEntityClass(ms);
StringBuilder sql = new StringBuilder();
// 如果设置了安全删除,就不允许执行不带查询条件的 delete 方法
if (getConfig().isSafeDelete()) {
sql.append(SqlHelper.notAllNullParameterCheck("_parameter", EntityHelper.getColumns(entityClass)));
}
// 如果是逻辑删除,则修改为更新表,修改逻辑删除字段的值
if (SqlHelper.hasLogicDeleteColumn(entityClass)) {
sql.append(SqlHelper.updateTable(entityClass, tableName(entityClass)));
sql.append("<set>");
String deleteMarkStr = SqlHelper.logicDeleteColumnEqualsValue(entityClass, true);
sql.append(deleteMarkStr);
sql.append("</set>");
MetaObjectUtil.forObject(ms).setValue("sqlCommandType", SqlCommandType.UPDATE);
} else {
sql.append(SqlHelper.deleteFromTable(entityClass, tableName(entityClass)));
}
sql.append(SqlHelper.whereAllIfColumns(entityClass, isNotEmpty()));
return sql.toString();
}
/**
* 通过主键删除
*
* @param ms
*/
public String deleteSoftByPrimaryKey(MappedStatement ms) {
final Class<?> entityClass = getEntityClass(ms);
StringBuilder sql = new StringBuilder();
if (SqlHelper.hasLogicDeleteColumn(entityClass)) {
sql.append(SqlHelper.updateTable(entityClass, tableName(entityClass)));
sql.append("<set>");
String deleteMarkStr = SqlHelper.logicDeleteColumnEqualsValue(entityClass, true);
sql.append(deleteMarkStr);
sql.append("</set>");
MetaObjectUtil.forObject(ms).setValue("sqlCommandType", SqlCommandType.UPDATE);
} else {
sql.append(SqlHelper.deleteFromTable(entityClass, tableName(entityClass)));
}
sql.append(SqlHelper.wherePKColumns(entityClass));
return sql.toString();
}
/**
* 根据Example删除
*
* @param ms
* @return
*/
public String deleteSoftByExample(MappedStatement ms) {
Class<?> entityClass = getEntityClass(ms);
StringBuilder sql = new StringBuilder();
if (isCheckExampleEntityClass()) {
sql.append(SqlHelper.exampleCheck(entityClass));
}
// 如果设置了安全删除,就不允许执行不带查询条件的 delete 方法
if (getConfig().isSafeDelete()) {
sql.append(SqlHelper.exampleHasAtLeastOneCriteriaCheck("_parameter"));
}
if (SqlHelper.hasLogicDeleteColumn(entityClass)) {
sql.append(SqlHelper.updateTable(entityClass, tableName(entityClass)));
sql.append("<set>");
String deleteMarkStr = SqlHelper.logicDeleteColumnEqualsValue(entityClass, true);
sql.append(deleteMarkStr);
sql.append("</set>");
MetaObjectUtil.forObject(ms).setValue("sqlCommandType", SqlCommandType.UPDATE);
} else {
sql.append(SqlHelper.deleteFromTable(entityClass, tableName(entityClass)));
}
sql.append(SqlHelper.exampleWhereClause());
return sql.toString();
}
/**
* 根据主键字符串进行删除,类中只有存在一个带有@Id注解的字段
*
* @param ms
* @return
*/
public String deleteSoftByIds(MappedStatement ms) {
final Class<?> entityClass = getEntityClass(ms);
if (SqlHelper.hasLogicDeleteColumn(entityClass)) {
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.updateTable(entityClass, tableName(entityClass)));
sql.append("<set>");
String deleteMarkStr = SqlHelper.logicDeleteColumnEqualsValue(entityClass, true);
sql.append(deleteMarkStr);
sql.append("</set>");
Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
EntityColumn column = columnList.iterator().next();
sql.append(" where ");
sql.append(column.getColumn());
sql.append(" in (${_parameter})");
return sql.toString();
} else {
return deleteRealByIds(ms);
}
}
/**
* 根据主键字符串进行删除,类中只有存在一个带有@Id注解的字段
*
* @param ms
* @return
*/
public String deleteRealByIds(MappedStatement ms) {
final Class<?> entityClass = getEntityClass(ms);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.deleteFromTable(entityClass, tableName(entityClass)));
Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
if (columnList.size() == 1) {
EntityColumn column = columnList.iterator().next();
sql.append(" where ");
sql.append(column.getColumn());
sql.append(" in (${_parameter})");
} else {
throw new MapperException(
"继承 deleteByIds 方法的实体类[" + entityClass.getCanonicalName() + "]中必须只有一个带有 @Id 注解的字段");
}
return sql.toString();
}
}
加上之后果然删除就生效了,与此同时我们可以发现在底层使用的删除方法的sql中采取的方式的是SqlCommandType.UPDATE,因此删除的方式为逻辑删除,如果需要将数据直接从库里清除,可以采用将SqlCommandType改为SqlCommandType.DELETE
public enum SqlCommandType {
UNKNOWN,
INSERT,
UPDATE,
DELETE,
SELECT,
FLUSH;
private SqlCommandType() {
}
}
从这点小事情我们可以发现,任何一个成熟的工具使用都应该建立在对起有足够的了解上,只是盲目的去使用网上所说的工具可能会导致出了问题排查很久都没有效果。