MyBatis 3.3.1版本新功能示例
MyBatis3.3.1更新日志:
https://github.com/mybatis/mybatis-3/issues?q=milestone%3A3.3.1
这里不对更新做翻译或者其他详细介绍。
这个更新除了一些bug修复,还有两个新增的功能:
- 增加了对批量插入回写自增主键的功能
- 增加了注解引用
@Results
的功能
下面通过简单例子来介绍这两个功能,为了例子的简洁,这里都使用注解实现的,没有用XML,批量插入的例子很容易就能变成XML形式的,大家自己尝试。
先看基础的表和对应的POJO。
city表:
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for city
-- ----------------------------
DROP TABLE IF EXISTS `city`;
CREATE TABLE `city` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`state` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of city
-- ----------------------------
INSERT INTO `city` VALUES ('1', '石家庄', '河北');
INSERT INTO `city` VALUES ('2', '邯郸', '河北');
city对象:
public class City2 {
private Integer id;
private String cityName;
private String cityState;
public City2() {
}
public City2(String cityName, String cityState) {
this.cityName = cityName;
this.cityState = cityState;
}
//省略setter,getter
@Override
public String toString() {
return "City2{" +
"id=" + id +
", cityName='" + cityName + '\'' +
", cityState='" + cityState + '\'' +
'}';
}
}
定义如下MyBatis331Mapper
接口
/**
* mybatis3.3.1版本新增功能测试
*
* @author liuzh
* @since 2016-03-06 17:22
*/
public interface MyBatis331Mapper {
/**
* 批量插入
*
* @param cities
* @return
*/
@Insert("<script>" +
"insert into city (id, name, state) values " +
"<foreach collection=\"list\" item=\"city\" separator=\",\" >" +
"(#{city.id}, #{city.cityName}, #{city.cityState})" +
"</foreach>" +
"</script>")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertCities(List<City2> cities);
/**
* 根据主键查询一个
*
* @param id
* @return
*/
@Results(id = "cityResult", value = {
@Result(property = "id", column = "id", id = true),
@Result(property = "cityName", column = "name", id = true),
@Result(property = "cityState", column = "state", id = true)
})
@Select("select id, name, state from city where id = #{id}")
City2 selectByCityId(Integer id);
/**
* 查询全部,引用上面的Results
*
* @return
*/
@ResultMap("cityResult")
@Select("select id, name, state from city")
List<City2> selectAll();
}
这里详细说一下这两个新功能的用法。
先看批量插入的例子
@Insert("<script>" +
"insert into city (id, name, state) values " +
"<foreach collection=\"list\" item=\"city\" separator=\",\" >" +
"(#{city.id}, #{city.cityName}, #{city.cityState})" +
"</foreach>" +
"</script>")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertCities(List<City2> cities);
首先接口参数只能有一个(默认情况下),如果你参数有多个,那么要返回主键的那个List
必须加注解@Param("list")
或者在参数Map
中对应的key
为"list"
。这一点很重要,只有看源码才能了解(当然除了"list"
还有另外的名字,例如支持数组的"array"
),参考Jdbc3KeyGenerator
类中的这段代码:
if (parameterMap.containsKey("collection")) {
parameters = (Collection) parameterMap.get("collection");
} else if (parameterMap.containsKey("list")) {
parameters = (List) parameterMap.get("list");
} else if (parameterMap.containsKey("array")) {
parameters = Arrays.asList((Object[]) parameterMap.get("array"));
}
然后就是必须使用useGeneratedKeys
的方式,注解使用下面的方式:
@Options(useGeneratedKeys = true, keyProperty = "id")
XML使用类似下面的方式:
<insert id="insertList" useGeneratedKeys="true" keyProperty="id">
只要注意上面这几点,批量插入应该就能返回自增的值了。
注意:大家应该能理解,自增的不一定是主键,而且一个表中可能有多个自增的值。这些情况下都能获取到,keyProperty
需要设置多个属性值,逗号隔开即可。
再看引用@Results
(此功能是否为新增功能,我并不确定,因为我平时不用注解)
用MyBatis的人中,使用注解的是少数,但是有些企业由于领导或者别的原因,会限制必须用注解。
这对一些复杂的情况来说,使用起来不如XML的方便,但是不得不用。
以前如果返回一个对象的属性需要配置映射,那么每个对象上都需要这段重复的代码,看起来很乱很麻烦。
在上面的例子中,在selectByCityId
上定义了Results
,在下面的方法selectAll
上通过@ResultMap("cityResult")
直接引用的上面的Results
。这个功能在使用的时候没有特别注意的地方。
测试
写个简单的测试,代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@Transactional
@SpringApplicationConfiguration(Application.class)
public class MyBatis331Test {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private MyBatis331Mapper mapper;
@Test
@Rollback
public void testInsertList() {
List<City2> city2List = new ArrayList<City2>();
city2List.add(new City2("石家庄", "河北"));
city2List.add(new City2("邯郸", "河北"));
city2List.add(new City2("秦皇岛", "河北"));
Assert.assertEquals(3, mapper.insertCities(city2List));
for (City2 c2 : city2List) {
logger.info(c2.toString());
Assert.assertNotNull(c2.getId());
}
}
@Test
public void testSelectById(){
City2 city2 = mapper.selectByCityId(1);
logger.info(city2.toString());
Assert.assertNotNull(city2);
Assert.assertNotNull(city2.getCityName());
Assert.assertNotNull(city2.getCityState());
}
@Test
public void testSelectAll(){
List<City2> city2List = mapper.selectAll();
for(City2 c2 : city2List){
logger.info(c2.toString());
Assert.assertNotNull(c2);
Assert.assertNotNull(c2.getCityName());
Assert.assertNotNull(c2.getCityState());
}
}
}
第一个测试方法输出的部分日志如下:
==> Preparing: insert into city (id, name, state) values (?, ?, ?) , (?, ?, ?) , (?, ?, ?)
==> Parameters: null, 石家庄(String), 河北(String), null, 邯郸(String), 河北(String), null, 秦皇岛(String), 河北(String)
<== Updates: 3
City2{id=6, cityName='石家庄', cityState='河北'}
City2{id=7, cityName='邯郸', cityState='河北'}
City2{id=8, cityName='秦皇岛', cityState='河北'}
后两个方法输出的部分日志如下:
==> Preparing: select id, name, state from city where id = ?
==> Parameters: 1(Integer)
<== Total: 1
City2{id=1, cityName='石家庄', cityState='河北'}
==> Preparing: select id, name, state from city
==> Parameters:
<== Total: 2
City2{id=1, cityName='石家庄', cityState='河北'}
City2{id=2, cityName='邯郸', cityState='河北'}
注:由于批量插入事务并没有提交,因此这里查询出来的结果就是表中原有的两条数据。
最后
为了方便尝试上面的代码,可以直接查看下面项目的src/test
:
MyBatis-Spring-Boot: https://github.com/abel533/MyBatis-Spring-Boot
另外mybatis-spring项目也同时更新到了1.2.4
,这个版本对于使用SpringBoot的开发人员非常有用,这个版本解决了mybatis的循环依赖异常,如果你在使用SpringBoot,赶紧升级到最新的版本试试吧。