最近工作中用到了mybatis的Java API方式进行开发,顺便也整理下该功能的用法,接下来会针对基本部分进行学习:

1)Java API处理一对多、多对一的用法

2)增、删、改、查的用法;

3)涉及到类型转化的用法;

4)批量修改、批量查询、批量新增的用法(介绍的方案只使用于小批量数据处理,如果大批量数据处理,还需要使用ExecutorType.BATCH)。

Mybatis官网给了具体的文档,但是并没有对以上用法具体介绍,因此在这里整理下,以便以后工作用到时,可以参考。

本章主要使用Mybatis中使用typeHandlers进行对Enum进行转化的用法(本章将结合Spring自动注入《Spring(二十三):Spring自动注入的实现方式》)

本章将分为以下几部分:

1)环境搭建(maven+mybatis+spring整合);

2)查询、批量查询;

3)新增、批量新增;

4)修改、批量修改;

5)删除、批量删除。

下面我们针对每个步骤进行详细讲解。

1)环境搭建(maven+mybatis+spring整合)

新建maven项目 Learn-Spring-01,并在pom.xml导入mybatis/spring/mysql/druid/junit包,完整的pom.xml文章如下:

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.dx.test</groupId>
<artifactId>Learn-Spring-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>Learn-Spring-01</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!--Spring版本号 -->
<org.springframework.version>5.2.0.RELEASE</org.springframework.version>
<org.mybatis.version>3.4.6</org.mybatis.version>
<com.alibaba.version>1.1.21</com.alibaba.version>
<mysql.version>8.0.11</mysql.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
<!-- Sping Test相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
</dependency> <!--MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${org.mybatis.version}</version>
</dependency>
<!-- Mybatis自身实现的Spring整合依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency> <!--MySql数据库驱动 -->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${com.alibaba.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

在/src/main/resources下新建jdbc.properties/mybatis-config.xml/spring-config.xml配置文件:

jdbc.properties

#jdbc settings
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
jdbc.username=root
jdbc.password=123456 #pool settings
jdbc.pool.init=1
jdbc.pool.minIdle=3
jdbc.pool.maxActive=20 #jdbc.testSql=SELECT 'x'
jdbc.testSql=SELECT 'x' FROM DUAL

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置全局属性-->
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载(即按需加载) -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 打开全局缓存开关(二级缓存)默认值就是 true -->
<setting name="cacheEnabled" value="true"/>
<!--使用jdbc的getGeneratekeys获取自增主键值-->
<setting name="useGeneratedKeys" value="true"/>
<!--使用列别名替换别名  默认true select name as title form table; -->
<setting name="useColumnLabel" value="true"/>
<!--开启驼峰命名转换-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--打印sql日志-->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings> <!-- 引用db.properties配置文件 -->
<!--
<properties resource="db.properties"/>
<typeAliases>
<package name="com.dx.test.model"/>
</typeAliases>
--> <!-- 元素类型为 "configuration" 的内容必须匹配 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"。-->
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.dx.test.model.enums.ModuleType"/>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.dx.test.model.enums.OperateType"/>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.dx.test.model.enums.DataStatus"/>
</typeHandlers> <!-- 对事务的管理和连接池的配置 -->
<!--
<environments default="mysql_jdbc">
<environment id="oracle_jdbc">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${name}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment> <environment id="mysql_jdbc">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${name}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
--> <!-- 当使用了xxxMapper.xml时,需要配置这些配置 -->
<!--
<mappers>
<mapper resource="resources/mapper/TaskAutoExecutePlanMapper.xml"/>
</mappers>
-->
<!-- 因在Mapper类上加入了@Mapper注解,因此这个配置也不需要了。 -->
<!--
<mappers>
<mapper class="com.dx.test.mapper.ArticleCategoryMapper"></mapper>
<mapper class="com.dx.test.mapper.ArticleMapper"></mapper>
</mappers>
-->
</configuration>

spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd "> <!-- bean annotation driven -->
<context:annotation-config />
<context:component-scan base-package="com.dx.test.repository,com.dx.test.mapper,com.dx.test.service" /> <!-- 配置整合Mybatis过程 -->
<!-- 配置数据库相关参数 properties的属性:${url} -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
--> <!--2.配置连接池属性 -->
<!-- 数据源配置, 使用 Druid 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass -->
<property name="driverClassName" value="${jdbc.driver}" /> <!-- 基本属性 url、user、password -->
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" /> <!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${jdbc.pool.init}" />
<property name="minIdle" value="${jdbc.pool.minIdle}" />
<property name="maxActive" value="${jdbc.pool.maxActive}" /> <!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" /> <property name="validationQuery" value="${jdbc.testSql}" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小(Oracle使用) -->
<!-- <property name="poolPreparedStatements" value="true"/> <property name="maxPoolPreparedStatementPerConnectionSize"
value="20"/> -->
<!-- 配置监控统计拦截的filters: 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall -->
<property name="filters" value="stat,wall" />
</bean> <!--3.配置SqlSessionFactory对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据库连接池 -->
<property name="dataSource" ref="dataSource" />
<!--配置mybatis全局配置文件:mybatis-config.xml -->
<property name="configLocation"
value="classpath:mybatis-config.xml" />
<!-- 因为我们这采用的是Mybatis Java API方式,因此不需要配置 -->
<!--扫描entity包,使用别名,多个用;隔开 -->
<!-- <property name="typeAliasesPackage" value="com.dx.test.model"/> -->
<!--扫描sql配置文件:mapper需要的xml文件 -->
<!-- <property name="mapperLocations" value="classpath:mapper/*.xml"/> -->
</bean> <!-- Mapper接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dx.test.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean> <!-- 如果想对单个mapper类关联上sqlSessionFactory,可以这么使用,具体参考:http://mybatis.org/spring/getting-started.html -->
<!-- <bean id="articlerMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.dx.test.dao.mapper.ArticleMapper"
/> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> --> <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>

在src/main/java/com.dx.test.module.enum包下新建DataStatus枚举类:

package com.dx.test.model.enums;

public enum DataStatus {
Living(0), // 启用
UnUsed(1); // 作废 private int value; DataStatus(int value) {
this.value = value;
} public int getValue() {
return this.value;
}
}

在src/main/java/com.dx.test.model包下创建Log.java实体类:

package com.dx.test.model;

import java.util.Date;

import com.dx.test.model.enums.DataStatus;

/**
* 文章分类
* */
public class ArticleCategory {
private Integer id; //文章分类id
private String title; // 文章分类名称
private String imgSrc; // 文章分类banner图片
private String description; // 文章分类描述
private DataStatus state; // 记录状态 private String createUser; // 新建用户
private String createUserId;// 新建用户id
private Date createTime; // 新建时间
private String updateUser; // 修改用户
private String updateUserId;// 修改用户id
private Date updateTime; // 修改时间
private Integer version; // 乐观锁版本号 public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImgSrc() {
return imgSrc;
}
public void setImgSrc(String imgSrc) {
this.imgSrc = imgSrc;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public DataStatus getState() {
return state;
}
public void setState(DataStatus state) {
this.state = state;
}
public String getCreateUser() {
return createUser;
}
public void setCreateUser(String createUser) {
this.createUser = createUser;
}
public String getCreateUserId() {
return createUserId;
}
public void setCreateUserId(String createUserId) {
this.createUserId = createUserId;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateUser() {
return updateUser;
}
public void setUpdateUser(String updateUser) {
this.updateUser = updateUser;
}
public String getUpdateUserId() {
return updateUserId;
}
public void setUpdateUserId(String updateUserId) {
this.updateUserId = updateUserId;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
@Override
public String toString() {
return "ArticleCategory [id=" + id + ", title=" + title + ", imgSrc=" + imgSrc + ", description=" + description
+ "]";
}
}

在src/main/java/com.dx.test.mapper包下新建ArticleCategoryMapper.java mapper接口,目前该接口内容为空,在接口上添加上@Mapper注解。

package com.dx.test.mapper;
import org.apache.ibatis.annotations.Mapper; public interface ArticleCategoryMapper{ }

在src/main/java/com.dx.test.mapper.sqlprovider下新建ArticleCategorySqlProvider.java mapper sql生成类:

package com.dx.test.mapper.sqlprovider;

public class ArticleCategorySqlProvider {

}

在src/main/test/com.dx.test下新建测试类ArticleCategoryTest.java

package com.dx.test;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.dx.test.mapper.ArticleCategoryMapper; /**
* Unit test for simple App.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:spring-config.xml" })
public class ArticleCategroyTest {
@Autowired
private ArticleCategoryMapper articleCategoryMapper; }

2)查询、批量查询

在mapper类中新建查询、批量查询接口:

    /**
* +根据文章分类id,查询文件分类详情
* @param id 文章分类id
* @return 查询到的文章分类详情
* */
@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE, timeout = 60000)
@Results(id="articleCategoryResult",value = {
@Result(property = "id",column = "id",id = true),
@Result(property = "title",column = "title"),
@Result(property = "imgSrc",column = "img_src"),
@Result(property = "status",column = "status",javaType = DataStatus.class),
@Result(property = "description",column = "description"),
@Result(property = "createUser",column = "create_user"),
@Result(property = "createUserId",column = "create_user_id"),
@Result(property = "createTime",column = "create_time"),
@Result(property = "updateUser",column = "update_user"),
@Result(property = "updateUserId",column = "update_user_id"),
@Result(property = "updateTime",column = "update_time"),
@Result(property = "version",column = "version")
})
@Select("select * from article_category where id=#{id}")
ArticleCategory getById(Integer id); /**
* 根据id列表,查询多个文章分类,返回文章分类列表
*
* @param list 查询id列表
* @return returnResult
* */
@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE, timeout = 60000)
@ResultMap(value ="articleCategoryResult")
@SelectProvider(type = ArticleCategorySqlProvider.class, method = "getByIds")
List<ArticleCategory> getByIds(@Param("list") List<Integer> list); /**
* 根据过滤条件,查询返回满足条件的文章分类id列表
*
* @param articleCategory 查询条件
* @return returnResult
* */
@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE, timeout = 60000)
@Select(value="select * from `article_category` where state=#{state} and `title` like CONCAT(CONCAT('%', #{title}), '%')")
List<Integer> getIdsByCondition(ArticleCategory articleCategory); /**
* +根据查询条件,查询记录。
* @param articleCategory 查询条件
* @return 返回查询结果
* */
@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE, timeout = 60000)
@ResultMap(value ="articleCategoryResult")
@SelectProvider(type = ArticleCategorySqlProvider.class, method = "queryList")
List<ArticleCategory> queryList(ArticleCategory articleCategory); /**
* +根据查询条件,查询记录。
* @param articleCategory 查询条件
* @return 返回查询结果
* */
@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE, timeout = 60000)
@ResultMap(value ="articleCategoryResult")
@Select(value="select * from `article_category` where state=#{state}")
List<ArticleCategory> queryListByState(DataStatus state);

查询返回Map的用法:

    /**
* +根据查询条件,查询记录。
*
* @param articleCategory 查询条件
* @return 返回查询结果
*/
@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE, timeout = 60000)
@ResultType(value = HashMap.class)
@MapKey(value="id")
@Select(value = "select * from `article_category` where state=#{state}")
Map<Integer, ArticleCategory> getIdVsModelMap(DataStatus state); /**
* +根据查询条件,查询记录。
*
* @param articleCategory 查询条件
* @return 返回查询结果
*/
@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE, timeout = 60000)
@ResultType(value = HashMap.class)
// @Results(id="idVsTitleMapResult",
// value= {
// @Result(property = "key",column = "id"),
// @Result(property = "value",column = "title")
// }
// )
@Select(value = "select id,title from `article_category` where state=#{state}")
void getIdVsTitleMap(DataStatus state); /**
* 根据条件查询记录结果,返回Map{key:id,value:title}
* @param state 文章分类记录状态
* @return returnResult
* */
@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE, timeout = 60000)
@ResultType(value = HashMap.class)
@Select(value = "select id,title from `article_category` where state=#{state}")
Map<Integer,String> getIdVsTitleMapWithResultHandler(@Param("state") DataStatus state, ResultHandler<?> resultHandler);

在mapper sql provider类中新建sql帮助方法:

     /**
* +根据id集合,获取文章分类列表 SQL
*
* @param map map
* @return returnResult
*/
public String getByIds(final Map<String, List<Integer>> map) {
List<Integer> tmpList = map.get("list");
if (tmpList == null || tmpList.isEmpty()) {
return null;
} StringBuilder sql = new StringBuilder("SELECT * FROM `article_category` WHERE `id` in (");
for (int i = 0; i < tmpList.size(); i++) {
sql.append("#{list[" + i + "]},");
}
sql.deleteCharAt(sql.length() - 1);
sql.append(")");
sql.append(";"); return sql.toString();
} /**
* 根据查询条件,查询记录
*
* @param articleCategory 查询条件
* @return 返回查询结果
* */
public String queryList(ArticleCategory articleCategory) {
StringBuilder sql = new StringBuilder();
sql.append("select * from `article_category` where 1=1 "); if (articleCategory.getId() != null) {
sql.append(" AND `id`=#{id}");
}
if (articleCategory.getTitle() != null) {
sql.append(" AND `title` like CONCAT(CONCAT('%', #{title}), '%')");
}
if(articleCategory.getState()!=null) {
sql.append(" AND `state` = #{state}");
}
if (articleCategory.getCreateTime() != null) {
sql.append(" AND `create_time` = #{createTime}");
}
if (articleCategory.getCreateUser() != null) {
sql.append(" AND `create_user` = #{createUser}");
}
if (articleCategory.getCreateUserId() != null) {
sql.append(" AND `create_user_id` = #{createUserId}");
}
if (articleCategory.getUpdateTime() != null) {
sql.append(" AND `update_time` = #{updateTime}");
}
if (articleCategory.getUpdateUser() != null) {
sql.append(" AND `update_user` = #{updateUser}");
}
if (articleCategory.getUpdateUserId() != null) {
sql.append(" AND `update_user_id` = #{updateUserId}");
}
if (articleCategory.getVersion() != null) {
sql.append(" AND `version` = #{version}");
}
sql.append(" ORDER BY `id` DESC"); return sql.toString();
}

在测试类中新增测试方法:

    private ArticleCategory articleCategory;
private final String title = "category test title";
private final String titleNew = "category test title new"; @Test
public void testGetById() {
ArticleCategory articleCategory = this.articleCategoryMapper.getById(this.articleCategory.getId());
Assert.assertEquals(articleCategory.getId(), this.articleCategory.getId());
Assert.assertEquals(articleCategory.getTitle(), this.title);
} @Test
public void testGetByIds() {
List<Integer> idList = new ArrayList<Integer>(Arrays.asList(5, 6));
List<ArticleCategory> queryList = this.articleCategoryMapper.getByIds(idList);
Assert.assertEquals(queryList.size(), idList.size());
} @Test
public void testGetIdsByCondition() {
ArticleCategory articleCategory = new ArticleCategory();
articleCategory.setState(DataStatus.Living);
articleCategory.setTitle("test"); List<Integer> idList = this.articleCategoryMapper.getIdsByCondition(articleCategory);
Assert.assertTrue(idList.isEmpty() == false);
} @Test
public void testGetLivingIds() {
List<ArticleCategory> articleCategoryList=this.articleCategoryMapper.queryListByState(DataStatus.Living);
Assert.assertTrue(articleCategoryList.isEmpty() == false);
} @Test
public void testQueryList() {
ArticleCategory queryArticleCategory = new ArticleCategory();
queryArticleCategory.setTitle("test"); List<ArticleCategory> queryResultList = this.articleCategoryMapper.queryList(queryArticleCategory);
Assert.assertFalse(queryResultList.size() == 0);
}

map测试代码:

    @Test
public void testGetIdVsModelMap() {
Map<Integer,ArticleCategory> queryItems=this.articleCategoryMapper.getIdVsModelMap(DataStatus.Living);
Assert.assertTrue(queryItems.size()>0);
} @Autowired
private SqlSessionFactory sqlSessionFactory; @Test
public void testGetIdVsTitleMap() {
MapResultHandler mapResultHandler=new MapResultHandler();
this.sqlSessionFactory.openSession().select("com.dx.test.mapper.ArticleCategoryMapper.getIdVsTitleMap", DataStatus.Living,mapResultHandler);
@SuppressWarnings("unchecked")
Map<Integer, String> queryMap=mapResultHandler.getMappedResults(); Assert.assertTrue(queryMap.size()>0);
} @Test
public void testGetIdVsTitleMapWithResultHandler() {
MapResultHandler mapResultHandler=new MapResultHandler();
this.articleCategoryMapper.getIdVsTitleMapWithResultHandler(DataStatus.Living, mapResultHandler);
Assert.assertTrue(mapResultHandler.getMappedResults().size()>0);
}

注意:上边返回map的用法中,如果是非Map<Integer,Model>的模式时,需要使用ResultHandler来辅助实现:

package com.dx.test.mapper.resulthandlers;

import java.util.HashMap;
import java.util.Map; import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler; public class MapResultHandler implements ResultHandler {
private final Map mappedResults = new HashMap(); @Override
public void handleResult(ResultContext context) {
@SuppressWarnings("rawtypes")
Map map = (Map) context.getResultObject();
mappedResults.put(map.get("key"), map.get("value"));
} public Map getMappedResults() {
return mappedResults;
}
}

3)新增、批量新增

在mapper类中新建查询、批量查询接口:

    /**
* +入库文章分类
*
* @param articleCategory 待入库实体
* @return 影响条数
*/
@Options(useCache = true, flushCache = Options.FlushCachePolicy.TRUE, useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
@InsertProvider(type = ArticleCategorySqlProvider.class, method = "insert")
int insert(ArticleCategory articleCategory);
/**
* +批量添加记录
*
* @param articleCategoryList 文章分类列表
* @return returnResult
*/
@Options(useCache = true, flushCache = Options.FlushCachePolicy.TRUE, useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
@InsertProvider(type = ArticleCategorySqlProvider.class, method = "batchInsert")
int batchInserts(@Param("list") final List<ArticleCategory> articleCategoryList); /**
* +批量添加记录
*
* @param articleCategoryList 文章分类列表
* @return returnResult
*/
@Options(useCache = true, flushCache = Options.FlushCachePolicy.TRUE, useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
@Insert(value = "<script>"
+ "INSERT INTO `article_category`"
+ "(`title`,`img_src`,`description`,`state`,`create_time`,`create_user`,`create_user_id`,`update_time`,`update_user`,`update_user_id`,`version`)"
+ "VALUES"
+ "<foreach collection=\"list\" item=\"item\" index=\"index\" separator=\",\">"
+ " (#{item.title},#{item.imgSrc},#{item.description},#{item.state},now(),#{item.createUser},#{item.createUserId},now(),#{item.updateUser},#{item.updateUserId},0)"
+ "</foreach>"
+ "ON DUPLICATE KEY UPDATE `update_time` = now()"
+ "</script>")
int batchInsertsWithScript(@Param("list") final List<ArticleCategory> articleCategoryList);

在mapper sql provider类中新建sql帮助方法:

    /**
* 生成插入文章分类的SQL
*
* @param articleCategory文章分类
* @return 返回插入文章SQL
*/
public String insert(ArticleCategory articleCategory) {
return new SQL() {
{
INSERT_INTO("article_category");
INTO_COLUMNS("title", "img_src", "description","state", "create_user", "create_user_id", "create_time","update_user", "update_user_id", "update_time", "version");
INTO_VALUES("#{title}", "#{imgSrc}", "#{description}", "#{state}", "#{createUser}", "#{createUserId}", "now()","#{updateUser}", "#{updateUserId}", "now()", "0");
}
}.toString();
} /**
* 生成批量新增SQL
*
* @param map 参数信息
* @return 生成批量插入SQL语句
* */
public String batchInsert(Map<String, List<ArticleCategory>> map) {
List<ArticleCategory> tmpList = map.get("list");
if (tmpList == null || tmpList.isEmpty()) {
return null;
} StringBuilder sql = new StringBuilder("");
sql.append("INSERT INTO `article_category`");
sql.append("(`title`,`img_src`,`description`,`state`,`create_time`,`create_user`,`create_user_id`,`update_time`,`update_user`,`update_user_id`,`version`)");
sql.append("VALUES");
for (int i = 0; i < tmpList.size(); i++) {
sql.append("(#{list[" + i + "].title},#{list[" + i + "].imgSrc},#{list[" + i + "].description},#{list["+i+"].state},now(),#{list[" + i + "].createUser},#{list[" + i + "].createUserId},now(),#{list[" + i + "].updateUser},#{list[" + i + "].updateUserId},0),");
}
sql.deleteCharAt(sql.length() - 1);
sql.append(" ON DUPLICATE KEY UPDATE `update_time` = now()");
sql.append(";"); return sql.toString();
}

在测试类中新增测试方法:

    @Test
public void testInsert() {
ArticleCategory articleCategory = new ArticleCategory();
articleCategory.setTitle(title);
articleCategory.setDescription("category description");
articleCategory.setImgSrc("http://www.test.com/img/category/img-" + new Random().nextInt(1000) + ".gif");
articleCategory.setState(DataStatus.Living);
articleCategory.setCreateTime(new Date());
articleCategory.setCreateUser("create user");
articleCategory.setCreateUserId("user-" + new Random().nextInt(1000)); int result = this.articleCategoryMapper.insert(articleCategory);
this.articleCategory = articleCategory;
Assert.assertEquals(result, 1);
} @Test
public void testBatchInsert() {
List<ArticleCategory> articleCategoryList = new ArrayList<ArticleCategory>();
for (int i = 0; i < 10; i++) {
ArticleCategory articleCategory = new ArticleCategory();
articleCategory.setTitle(title + i);
articleCategory.setState(DataStatus.Living);
articleCategory.setDescription("category description");
articleCategory.setImgSrc("http://www.test.com/img/category/img-" + new Random().nextInt(1000) + ".gif");
articleCategory.setCreateTime(new Date());
articleCategory.setCreateUser("create user");
articleCategory.setCreateUserId("user-" + new Random().nextInt(1000)); articleCategoryList.add(articleCategory);
} int result = this.articleCategoryMapper.batchInserts(articleCategoryList);
Assert.assertEquals(result, 10);
} @Test
public void testBatchInsertWithScript() {
List<ArticleCategory> articleCategoryList = new ArrayList<ArticleCategory>();
for (int i = 0; i < 10; i++) {
ArticleCategory articleCategory = new ArticleCategory();
articleCategory.setTitle(title + i);
articleCategory.setState(DataStatus.Living);
articleCategory.setDescription("category description");
articleCategory.setImgSrc("http://www.test.com/img/category/img-" + new Random().nextInt(1000) + ".gif");
articleCategory.setCreateTime(new Date());
articleCategory.setCreateUser("create user");
articleCategory.setCreateUserId("user-" + new Random().nextInt(1000)); articleCategoryList.add(articleCategory);
} int result = this.articleCategoryMapper.batchInsertsWithScript(articleCategoryList);
Assert.assertEquals(result, 10);
}

4)修改、批量修改

在mapper类中新建查询、批量查询接口:

    /**
* +根据文章id,删除文章
*
* @param id 文章id
* @return 影响条数
*/
@Options(useCache = true, flushCache = Options.FlushCachePolicy.TRUE)
@UpdateProvider(type = ArticleCategorySqlProvider.class, method = "update")
int update(ArticleCategory articleCategory); /**
* +批量新增
* @param articleCategoryList 待修改对象
* @return 影响条数
* */
@Options(useCache = true, flushCache = Options.FlushCachePolicy.TRUE)
@UpdateProvider(type = ArticleCategorySqlProvider.class, method = "batchUpdate")
int batchUpdate(List<ArticleCategory> articleCategoryList); /**
* +批量新增
* @param articleCategoryList 待修改对象
* @return 影响条数
* */
@Options(useCache = true, flushCache = Options.FlushCachePolicy.TRUE)
@Update(value="<script>"
+ "<foreach item=\"item\" collection=\"list\" open=\"\" separator=\";\" close=\"\">"
+ "update `article_category` "
+ "<set>" +
" `id`=#{item.id} "
+ " <if test='item.title !=null and item.title != \"\"'>" +
" ,`title` = #{item.title} " +
" </if> " +
" <if test='item.imgSrc !=null'>" +
" ,`img_src` = #{item.imgSrc} " +
" </if> " +
" <if test='item.description !=null and item.description != \"\"'>" +
" ,`description` = #{item.description} " +
" </if> " +
" <if test='item.state !=null'>" +
" ,`state` = #{item.state} " +
" </if> " +
",`update_time` = now(), `update_user` = #{item.updateUser}, `update_user_id` = #{item.updateUserId}, `version` = `version` + 1 "
+ "</set>"
+ "where `id` = #{item.id} and `version`=#{item.version}"
+ "</foreach>"
+ "</script>")
int batchUpdateWithScript(List<ArticleCategory> articleCategoryList);

在mapper sql provider类中新建sql帮助方法:

    /**
* 生成修改文章分类SQL
*
* @param articleCategory 文章分类实体
* @return 返回修改文章分类SQL
*/
public String update(ArticleCategory articleCategory) {
StringBuilder sql = new StringBuilder(); sql.append("update `article_category` set id=#{id}");
if (articleCategory.getTitle() != null) {
sql.append(", `title`=#{title}");
}
if (articleCategory.getImgSrc() != null) {
sql.append(", `img_src`=#{imgSrc}");
}
if (articleCategory.getDescription() != null) {
sql.append(", `description`=#{description}");
}
if(articleCategory.getState()!=null) {
sql.append(", `state` = #{state}");
}
if (articleCategory.getCreateUser() != null) {
sql.append(", `create_user` = #{createUser}");
}
if (articleCategory.getCreateUserId() != null) {
sql.append(", `create_user_id` = #{createUserId}");
}
sql.append(", `update_time` = now()");
if (articleCategory.getUpdateUser() != null) {
sql.append(", `update_user` = #{updateUser}");
}
if (articleCategory.getUpdateUserId() != null) {
sql.append(", `update_user_id` = #{updateUserId}");
} sql.append(", `version` = `version` + 1");
sql.append(" WHERE `id` = #{id} AND `version` = #{version}"); return sql.toString();
} /**
* 生成批量更新SQL
*
* @param map 查询参数
* @return 返回生成的批量更新语句
* */
public String batchUpdate(Map<String, List<ArticleCategory>> map) {
List<ArticleCategory> tmpList = map.get("list");
if (tmpList == null || tmpList.isEmpty()) {
return null;
} StringBuilder sql = new StringBuilder(""); for (int i = 0; i < tmpList.size(); i++) {
ArticleCategory articleCategory=tmpList.get(i);
sql.append("update `article_category` set id=#{list["+i+"].id}");
if (articleCategory.getTitle() != null) {
sql.append(", `title`=#{list["+i+"].title}");
}
if (articleCategory.getImgSrc() != null) {
sql.append(", `img_src`=#{list["+i+"].imgSrc}");
}
if (articleCategory.getDescription() != null) {
sql.append(", `description`=#{list["+i+"].description}");
}
if(articleCategory.getState()!=null) {
sql.append(", `state` = #{list["+i+"].state}");
}
if (articleCategory.getCreateUser() != null) {
sql.append(", `create_user` = #{list["+i+"].createUser}");
}
if (articleCategory.getCreateUserId() != null) {
sql.append(", `create_user_id` = #{list["+i+"].createUserId}");
}
sql.append(", `update_time` = now()");
if (articleCategory.getUpdateUser() != null) {
sql.append(", `update_user` = #{list["+i+"].updateUser}");
}
if (articleCategory.getUpdateUserId() != null) {
sql.append(", `update_user_id` = #{list["+i+"].updateUserId}");
} sql.append(", `version` = `version` + 1");
sql.append(" WHERE `id` = #{list["+i+"].id} AND `version` = #{list["+i+"].version}");
sql.append(";");
} return sql.toString();
}

在测试类中新增测试方法:

    @Test
public void testUpdate() {
ArticleCategory articleCategory = this.articleCategoryMapper.getById(this.articleCategory.getId());
Assert.assertEquals(articleCategory.getId(), this.articleCategory.getId());
Assert.assertEquals(articleCategory.getTitle(), this.title); articleCategory.setTitle(this.titleNew);
int result = this.articleCategoryMapper.update(articleCategory);
Assert.assertEquals(result, 1); articleCategory = this.articleCategoryMapper.getById(this.articleCategory.getId());
Assert.assertEquals(articleCategory.getId(), this.articleCategory.getId());
Assert.assertEquals(articleCategory.getTitle(), this.title);
} @Test
public void testBatchUpdate() {
ArticleCategory queryArticleCategory = new ArticleCategory();
queryArticleCategory.setTitle("ccccc");
List<ArticleCategory> queryItems = this.articleCategoryMapper.queryList(queryArticleCategory);
for (ArticleCategory articleCategory : queryItems) {
articleCategory.setTitle("DDD");
} int result = this.articleCategoryMapper.batchUpdate(queryItems);
Assert.assertEquals(result, 1);
} @Test
public void testBatchUpdateWithScript() {
ArticleCategory queryArticleCategory = new ArticleCategory();
queryArticleCategory.setTitle("DDD");
List<ArticleCategory> queryItems = this.articleCategoryMapper.queryList(queryArticleCategory);
for (ArticleCategory articleCategory : queryItems) {
articleCategory.setTitle("ccccc");
} int result = this.articleCategoryMapper.batchUpdateWithScript(queryItems);
Assert.assertEquals(result, 1);
}

测试执行批量插入,按照上边jdbc.properties和spring-config.xml配置中会抛出以下异常:

Caused by: java.sql.SQLException: sql injection violation, multi-statement not allow : update `article_category` set id=?, `title`=?, `img_src`=?, `description`=?, `state` = ?, `create_user` = ?, `create_user_id` = ?, `update_time` = now(), `version` = `version` + 1 WHERE `id` = ? AND `version` = ?;update `article_category` set id=?, `title`=?, `img_src`=?, `description`=?, `state` = ?, `create_user` = ?, `create_user_id` = ?, `update_time` = now(), `version` = `version` + 1 WHERE `id` = ? AND `version` = ?;update `article_category` set id=?, `title`=?, `img_src`=?, `description`=?, `state` = ?, `create_user` = ?, `create_user_id` = ?, `update_time` = now(), `version` = `version` + 1 WHERE `id` = ? AND `version` = ?;update `article_category` set id=?, `title`=?, `img_src`=?, `description`=?, `state` = ?, `create_user` = ?, `create_user_id` = ?, `update_time` = now(), `version` = `version` + 1 WHERE `id` = ? AND `version` = ?;
at com.alibaba.druid.wall.WallFilter.checkInternal(WallFilter.java:808)
at com.alibaba.druid.wall.WallFilter.connection_prepareStatement(WallFilter.java:294)
at com.alibaba.druid.filter.FilterChainImpl.connection_prepareStatement(FilterChainImpl.java:610)
at com.alibaba.druid.filter.FilterAdapter.connection_prepareStatement(FilterAdapter.java:943)
at com.alibaba.druid.filter.FilterEventAdapter.connection_prepareStatement(FilterEventAdapter.java:143)
at com.alibaba.druid.filter.FilterChainImpl.connection_prepareStatement(FilterChainImpl.java:610)
at com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.prepareStatement(ConnectionProxyImpl.java:374)
at com.alibaba.druid.pool.DruidPooledConnection.prepareStatement(DruidPooledConnection.java:388)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.ibatis.logging.jdbc.ConnectionLogger.invoke(ConnectionLogger.java:55)
at com.sun.proxy.$Proxy38.prepareStatement(Unknown Source)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.instantiateStatement(PreparedStatementHandler.java:85)
at org.apache.ibatis.executor.statement.BaseStatementHandler.prepare(BaseStatementHandler.java:88)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.prepare(RoutingStatementHandler.java:59)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:85)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:426)
... 37 more

此时出现错误原因,多个sql语句执行被过滤器过滤掉了。

问题解决:

1)需要在jdbc.properties中url中添加allowMultiQueries=true

#jdbc settings
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb?allowMultiQueries=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
jdbc.username=root
jdbc.password=123456 #pool settings
jdbc.pool.init=1
jdbc.pool.minIdle=3
jdbc.pool.maxActive=20 #jdbc.testSql=SELECT 'x'
jdbc.testSql=SELECT 'x' FROM DUAL

2)在spring-config.xml中修改,添加wall-filter/stat-filter,并引入dataSource下。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd "> <!-- bean annotation driven -->
<context:annotation-config />
<context:component-scan base-package="com.dx.test.repository,com.dx.test.mapper,com.dx.test.service" /> <!-- 配置整合Mybatis过程 -->
<!-- 配置数据库相关参数 properties的属性:${url} -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
--> <!--2.配置连接池属性 -->
<!-- 数据源配置, 使用 Druid 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass -->
<property name="driverClassName" value="${jdbc.driver}" /> <!-- 基本属性 url、user、password -->
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" /> <!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${jdbc.pool.init}" />
<property name="minIdle" value="${jdbc.pool.minIdle}" />
<property name="maxActive" value="${jdbc.pool.maxActive}" /> <!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" /> <property name="validationQuery" value="${jdbc.testSql}" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小(Oracle使用) -->
<!-- <property name="poolPreparedStatements" value="true"/> <property name="maxPoolPreparedStatementPerConnectionSize"
value="20"/> -->
<property name="proxyFilters">
<list>
<ref bean="stat-filter" />
<ref bean="wall-filter" />
</list>
</property>
<!-- 配置监控统计拦截的filters: 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall -->
<property name="filters" value="stat,wall" />

</bean>
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
<property name="slowSqlMillis" value="30000" />
<property name="logSlowSql" value="true" />
<property name="mergeSql" value="true" />
</bean> <bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter">
<property name="dbType" value="mysql" />
<property name="config" ref="wall-config" />
</bean> <bean id="wall-config" class="com.alibaba.druid.wall.WallConfig">
<!-- 批量sql -->
<property name="multiStatementAllow" value="true" />
</bean>
<!--3.配置SqlSessionFactory对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据库连接池 -->
<property name="dataSource" ref="dataSource" />
<!--配置mybatis全局配置文件:mybatis-config.xml -->
<property name="configLocation"
value="classpath:mybatis-config.xml" />
<!-- 因为我们这采用的是Mybatis Java API方式,因此不需要配置 -->
<!--扫描entity包,使用别名,多个用;隔开 -->
<!-- <property name="typeAliasesPackage" value="com.dx.test.model"/> -->
<!--扫描sql配置文件:mapper需要的xml文件 -->
<!-- <property name="mapperLocations" value="classpath:mapper/*.xml"/> -->
</bean> <!-- Mapper接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dx.test.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean> <!-- 如果想对单个mapper类关联上sqlSessionFactory,可以这么使用,具体参考:http://mybatis.org/spring/getting-started.html -->
<!-- <bean id="articlerMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.dx.test.dao.mapper.ArticleMapper"
/> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> --> <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>

上边加粗本分是要修改的部分。

5)删除、批量删除

在mapper类中新建查询、批量查询接口:

    /**
* +根据文章分类id,删除文章分类
*
* @param id 文章分类id
* @return 影响条数
*/
@Options(useCache = true, flushCache = Options.FlushCachePolicy.TRUE)
@Delete("delete from article_category where id=#{id}")
int delete(Integer id); /**
* +根据文章分类id集合,删除文章分类
*
* @param list 文章分类id集合
* @return 影响条数
*/
@Options(useCache = true, flushCache = Options.FlushCachePolicy.TRUE)
@DeleteProvider(type = ArticleCategorySqlProvider.class, method = "batchDelete")
int batchDelete(@Param("list") List<Integer> list); /**
* +批量删除记录
*
* @param list 待删除id集合
* @return returnResult
*/
@Options(useCache = true, flushCache = Options.FlushCachePolicy.TRUE)
@Delete("<script>" +
"delete from `article_category` where `id` in " +
"<foreach collection=\"list\" index=\"i\" open=\"(\" separator=\",\" close=\")\" item=\"item\" >" +
"#{item}" +
"</foreach>" +
"</script>")
int batchDeleteWithScript(@Param("list") List<Integer> list);

在mapper sql provider类中新建sql帮助方法:

   /**
* +根据id集合,获取文章分类列表 SQL
*
* @param map map
* @return returnResult
*/
public String batchDelete(final Map<String, List<Integer>> map) {
List<Integer> tmpList = map.get("list");
if (tmpList == null || tmpList.isEmpty()) {
return null;
} StringBuilder sql = new StringBuilder("DELETE FROM `article_category` WHERE `id` in (");
for (int i = 0; i < tmpList.size(); i++) {
sql.append("#{list[" + i + "]},");
}
sql.deleteCharAt(sql.length() - 1);
sql.append(")");
sql.append(";"); return sql.toString();
}

在测试类中新增测试方法:

    @Test
public void testDelete() {
int result = this.articleCategoryMapper.delete(this.articleCategory.getId());
Assert.assertEquals(result, 1);
} @Test
public void testBatchDelete() {
List<Integer> idList = new ArrayList<Integer>(Arrays.asList(1, 2));
int result = this.articleCategoryMapper.batchDelete(idList);
Assert.assertEquals(result, idList.size());
} @Test
public void testBatchDeleteWithScript() {
List<Integer> idList = new ArrayList<Integer>(Arrays.asList(3, 4));
int result = this.articleCategoryMapper.batchDeleteWithScript(idList);
Assert.assertEquals(result, idList.size());
}

参考《MyBatis与Druid数据库连接池集成监控统计后WallFilterSQL注入异常问题处理方案


MyBatis(九):Mybatis Java API批量操作(增、删、改、查)的更多相关文章

  1. 好用的SQL TVP~~独家赠送[增-删-改-查]的例子

    以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化.  本系列主要是针对T-SQL的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础] ...

  2. iOS FMDB的使用(增,删,改,查,sqlite存取图片)

    iOS FMDB的使用(增,删,改,查,sqlite存取图片) 在上一篇博客我对sqlite的基本使用进行了详细介绍... 但是在实际开发中原生使用的频率是很少的... 这篇博客我将会较全面的介绍FM ...

  3. iOS sqlite3 的基本使用(增 删 改 查)

    iOS sqlite3 的基本使用(增 删 改 查) 这篇博客不会讲述太多sql语言,目的重在实现sqlite3的一些基本操作. 例:增 删 改 查 如果想了解更多的sql语言可以利用强大的互联网. ...

  4. django ajax增 删 改 查

    具于django ajax实现增 删 改 查功能 代码示例: 代码: urls.py from django.conf.urls import url from django.contrib impo ...

  5. ADO.NET 增 删 改 查

    ADO.NET:(数据访问技术)就是将C#和MSSQL连接起来的一个纽带 可以通过ADO.NET将内存中的临时数据写入到数据库中 也可以将数据库中的数据提取到内存中供程序调用 ADO.NET所有数据访 ...

  6. MVC EF 增 删 改 查

    using System;using System.Collections.Generic;using System.Linq;using System.Web;//using System.Data ...

  7. python基础中的四大天王-增-删-改-查

    列表-list-[] 输入内存储存容器 发生改变通常直接变化,让我们看看下面列子 增---默认在最后添加 #append()--括号中可以是数字,可以是字符串,可以是元祖,可以是集合,可以是字典 #l ...

  8. MyBatis的配置与使用(增,删,改,查)

    ---恢复内容开始--- Mybatis入门介绍 一.MyBatis介绍 什么是MyBtis? MyBatis 是一个简化和实现了 Java 数据持久化层(persistence layer)的开源框 ...

  9. Java操作MongoDB:连接&增&删&改&查

    1.连接 ①方式一 MongoClientOptions.Builder builder = MongoClientOptions.builder(); //可以通过builder做各种详细配置 Mo ...

  10. js数组的管理[增,删,改,查]

    今天在设计表单的时候遇到对数组的一些处理的问题,比如说怎么创建一个数组,然后牵扯到数组的增删改查的方法.请看API FF: Firefox, N: Netscape, IE: Internet Exp ...

随机推荐

  1. Ansible-概念

    控住节点 任何装有Ansible的机器.您可以从任何控制节点调用/usr/bin/ansible或来运行命令和剧本/usr/bin/ansible-playbook.您可以将任何安装了Python的计 ...

  2. Linux(CentOS7)下安装Mysql8数据库

    一.Linux版本 二.先下载Linux下的Mysql包,打开Mysql官网 https://www.mysql.com/ 点击DOWNLOAD,进入 https://www.mysql.com/do ...

  3. 浅谈Python设计模式 - 抽象工厂模式

    声明:本系列文章主要参考<精通Python设计模式>一书,并且参考一些资料,结合自己的一些看法来总结而来. 在上一篇我们对工厂模式中的普通工厂模式有了一定的了解,其实抽象工作就是 表示针对 ...

  4. 《linux就该这么学》课堂笔记03 命令初识 echo、date、reboot、poweroff、wget...

    Linux进程的六种状态(R.S.D.T.Z.X): R --- TASK_RUNNING(可执行状态) S --- TASK_INTERRUPTIBLE(可中断的睡眠状态) D --- TASK_U ...

  5. 自制Linux操作系统

    自制Linux操作系统 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.添加一块新的磁盘设备 1>.将虚拟机关机,点击"编辑虚拟机设置" 2>.点 ...

  6. 在ubuntu系统中,遇到 “由于/bin 不在PATH 环境变量中,故无法找到该命令”问题

    2018年01月07日 11:27:34 YangJianShuai 阅读数 3024更多 分类专栏: linux 好多命令的位置在/usr/bin.恢复办法如下:1. /usr/bin/sudo v ...

  7. 使用MPU6050陀螺仪自制Arduino数字量角器

    MPU6050惯性单元是一个3轴加速度计和一个3轴陀螺仪组合的单元.它还包含温度传感器和DCM,可执行复杂的任务. MPU6050通常用于制作无人机和其他远程控制机器人,如自平衡机器人.在本篇文章中, ...

  8. Android开发学习了这些,上帝都淘汰不了你

    曾听过很多人说Android学习很简单,做个App就上手了,工作机会多,毕业后也比较容易找工作.这种观点可能是很多Android开发者最开始入行的原因之一. 在工作初期,工作主要是按照业务需求实现Ap ...

  9. 织梦DedeCms网站内部SEO详细分析

    一.确定网站的目标关键词 最为重要的一方面,一般网站首页的目标关键词建议在1-3个.对于目标关键词的选取有几个建议 1.定位:网站的内容和业务相关 2.分析:对用户体验及竞争对手的网站进行分析 3.筛 ...

  10. Zipkin 的 Docker 镜像使用

    1.Zipkin 在 dockerhub 上网址:https://hub.docker.com/r/openzipkin/zipkin 2.下载镜像 docker pull openzipkin/zi ...