这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
书接上回,上一篇在Mybatis之在源码项目中连接MySQL测试一文中介绍了如何在Mybatis源码项目中连接MySQL进行测试。本文接上文的内容,介绍一下使用Mybatis的XML进行增删改查的方法。
Mybatis版本:3.5.2
基于POJO对象
增
- Mapper接口中的方法
/**
* 基于POJO的新增XML方式
*/
int insertXmlPojo(Purchase purchase);
- XML中的代码,这里的id与Mapper接口中的方法名必须要完全相同,参数类型则是POJO类的完全限定名,整个SQL语句使用
insert标签声明
<insert id="insertXmlPojo" parameterType="org.apache.ibatis.z_run.pojo.Purchase">
insert into purchase
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="name != null">
`name`,
</if>
<if test="price != null">
`price`,
</if>
<if test="category != null">
category,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="price != null">
#{price,jdbcType=INTEGER},
</if>
<if test="category != null">
#{category,jdbcType=INTEGER},
</if>
</trim>
</insert>
- 某些场景中,需要将新插入数据的自增主键ID进行返回,可以使用
useGeneratedKeys、keyColumn、keyProperty属性一起来实现
<insert id="insertXmlPojo" parameterType="org.apache.ibatis.z_run.pojo.Purchase" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into purchase
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="name != null">
`name`,
</if>
<if test="price != null">
`price`,
</if>
<if test="category != null">
category,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="price != null">
#{price,jdbcType=INTEGER},
</if>
<if test="category != null">
#{category,jdbcType=INTEGER},
</if>
</trim>
</insert>
- 测试类代码
@Test
public void insert() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
//组装参数
Purchase purchase = new Purchase();
purchase.setName("火腿");
purchase.setPrice(3);
purchase.setCategory(1);
mapper.insertXmlPojo(purchase);
System.out.println(purchase);
}
- 返回的主键ID
DEBUG [main] - ==> Preparing: insert into purchase ( `name`, `price`, category ) values ( ?, ?, ? )
DEBUG [main] - ==> Parameters: 火腿(String), 3(Integer), 1(Integer)
DEBUG [main] - <== Updates: 1
Purchase{id=10, name='火腿', price=3, category=1}
- 数据库中的数据
查
- Mapper接口中的方法
/**
* 基于POJO的查询XML方式
*/
Purchase findXmlPojoByID(Purchase purchase);
- XML中的方法
- 语句使用
select标签声明 - 要查询的字段使用sql标签进行提取,并在SQL语句中进行引用
- 返回值使用
resultMap进行映射,column属性表示数据库表中的字段名,property表示POJO类中的属性名 - 如果Mapper接口中的方法返回值是集合
List<Purchase>,XML代码中的resultMap写法也是如此,即resultMap的属性值是集合中元素的完全限定名
- 语句使用
<sql id="Base_Column_List">
id, `name`, price, category
</sql>
<resultMap id="BaseResultMap" type="org.apache.ibatis.z_run.pojo.Purchase">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="price" jdbcType="INTEGER" property="price" />
<result column="category" jdbcType="INTEGER" property="category" />
</resultMap>
<select id="findXmlPojoByID" parameterType="org.apache.ibatis.z_run.pojo.Purchase" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from purchase
where id = #{id,jdbcType=INTEGER}
</select>
- 测试类
@Test
public void query() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
//组装参数
Purchase purchase = new Purchase();
purchase.setId(6);
System.out.println(mapper.findXmlPojoByID(purchase));
}
- 查询结果
DEBUG [main] - ==> Preparing: select id, `name`, price, category from purchase where id = ?
DEBUG [main] - ==> Parameters: 6(Integer)
DEBUG [main] - <== Total: 1
Purchase{id=6, name='火腿', price=3, category=1}
注意:使用
resultMap进行结果集映射时,通常是在数据库列名与POJO属性名不同的情况下,本文中的数据库列名与POJO属性名是相同的,其实没必要写这个resultMap,这里只是做一个演示。
改
- Mapper接口中的方法
/**
* 基于POJO的修改XML方式
*/
int updateXmlPojoByID(Purchase purchase);
- XML中的代码,SQL语句使用
update标签声明
<update id="updateXmlPojoByID" parameterType="org.apache.ibatis.z_run.pojo.Purchase">
update purchase
<set>
<if test="name != null">
`name` = #{name,jdbcType=VARCHAR},
</if>
<if test="price != null">
`price` = #{price,jdbcType=INTEGER},
</if>
<if test="category != null">
category = #{category,jdbcType=INTEGER},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
- 测试类
@Test
public void update() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
//组装参数
Purchase purchase = new Purchase();
purchase.setId(6);
//将价格修改为6
purchase.setPrice(6);
mapper.updateXmlPojoByID(purchase);
}
删
- Mapper接口中的方法
/**
* 基于POJO的删除XML方式
*/
int deleteXmlPojoByID(Purchase purchase);
- XML中的代码,SQL使用
delete标签声明
<delete id="deleteXmlPojoByID" parameterType="org.apache.ibatis.z_run.pojo.Purchase">
delete from purchase where id = #{id,jdbcType=INTEGER}
</delete>
- 测试类
@Test
public void delete() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
//组装参数
Purchase purchase = new Purchase();
purchase.setId(6);
mapper.deleteXmlPojoByID(purchase);
}
基于POJO的方式是主要的方式,虽然需要维护参数POJO,但是代码的可读性较好,易于维护。
基于Map
- 基于Map的方式进行增删改查就十分的便捷了,只需要将参数中的POJO替换成Map即可
- Mapper接口中的方法
/**
* 新增:基于Map的XML方式
*/
int insertMapPojo(Map<String, Object> purchase);
/**
* 查询:基于Map的XML方式
*/
Purchase findMapPojoByID(Map<String, Object> purchase);
/**
* 修改:基于Map的XML方式
*/
int updateMapPojoByID(Map<String, Object> purchase);
/**
* 删除:基于Map的XML方式
*/
int deleteMapPojoByID(Map<String, Object> purchase);
- XML中的代码,与POJO传参方式的区别就是参数类型
parameterType,这里是使用的Map
<insert id="insertMapPojo" parameterType="java.util.Map">
insert into purchase
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="name != null">
`name`,
</if>
<if test="price != null">
`price`,
</if>
<if test="category != null">
category,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="price != null">
#{price,jdbcType=INTEGER},
</if>
<if test="category != null">
#{category,jdbcType=INTEGER},
</if>
</trim>
</insert>
<select id="findMapPojoByID" parameterType="java.util.Map" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from purchase
where id = #{id,jdbcType=INTEGER}
</select>
<update id="updateMapPojoByID" parameterType="java.util.Map">
update purchase
<set>
<if test="name != null">
`name` = #{name,jdbcType=VARCHAR},
</if>
<if test="price != null">
`price` = #{price,jdbcType=INTEGER},
</if>
<if test="category != null">
category = #{category,jdbcType=INTEGER},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<delete id="deleteMapPojoByID" parameterType="java.util.Map">
delete from purchase where id = #{id,jdbcType=INTEGER}
</delete>
- 测试类,在测试类中,所有的参数都使用Map来进行封装
@Test
public void insert() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
//组装参数
Map<String, Object> param = new HashMap<>();
param.put("name", "辣条");
param.put("price", "1");
param.put("category", "2");
mapper.insertMapPojo(param);
}
@Test
public void query() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
//组装参数
Map<String, Object> param = new HashMap<>();
param.put("id", 7);
System.out.println(mapper.findMapPojoByID(param));
}
@Test
public void update() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
//组装参数
Map<String, Object> param = new HashMap<>();
param.put("id", 7);
param.put("price", "3");
mapper.updateMapPojoByID(param);
}
@Test
public void delete() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
//组装参数
Map<String, Object> param = new HashMap<>();
param.put("id", 7);
mapper.deleteMapPojoByID(param);
}
基于Map传参的方式一般不使用,原因在于代码的可读性较差,如果只关注于Maper接口中的方法,完全不知道这个方法中传递的参数具体是什么。
基于注解
-
基于注解的方式一般用于参数较少(参数个数大于1,当参数个数等于1时,不需要使用注解)的操作中。
-
Mapper接口中的方法
List<Purchase> findByPriceAndCategory(@Param("price") Integer price, @Param("category") Integer category);
- XML中的代码,此时的参数类型不需要声明,当两个参数类型不一致的时候,无法声明。
<select id="findByPriceAndCategory" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from purchase
where price = #{price,jdbcType=INTEGER}
and category = #{category,jdbcType=INTEGER}
</select>
- 测试代码及结果
System.out.println(mapper.findByPriceAndCategory(3,1));
DEBUG [main] - ==> Preparing: select id, `name`, price, category from purchase where price = ? and category = ?
DEBUG [main] - ==> Parameters: 3(Integer), 1(Integer)
DEBUG [main] - <== Total: 1
[Purchase{id=1, name='可乐', price=3, category=1}]
在方法中有两个参数时,如果不加
Param注解,就会抛出以下异常。
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'price' not found. Available parameters are [arg1, arg0, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'price' not found. Available parameters are [arg1, arg0, param1, param2]
此时,由于没有指定参数的名称,在Mybatis中默认使用arg0,arg1(从0开始),或param1,param2(从1开始)来进行参数映射。上文中的SQL进行如下改写即可正常运行。
<select id="findByPriceAndCategory" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from purchase
where price = #{arg0,jdbcType=INTEGER}
and category = #{arg1,jdbcType=INTEGER}
</select>
或
<select id="findByPriceAndCategory" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from purchase
where price = #{param1,jdbcType=INTEGER}
and category = #{param2,jdbcType=INTEGER}
</select>
Tips
此外,在Maper接口中的方法,除了查询方法外,其他方法的返回值都是使用的int,因为这些方法执行完成后可以返回修改数据的条数,可以据此来判断操作是否成功。
- 例如,执行以下测试用例:
@Test
public void delete() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
//组装参数
Map<String, Object> param = new HashMap<>();
param.put("id", 7);
System.out.println(mapper.deleteMapPojoByID(param));
}
- 第一次执行,删除了一条数据,返回值为1
DEBUG [main] - ==> Preparing: delete from purchase where id = ?
DEBUG [main] - ==> Parameters: 7(Integer)
DEBUG [main] - <== Updates: 1
1
- 第二次执行,数据在第一次操作中已经被删除了,本次执行没有删除数据,因此返回值为0
DEBUG [main] - ==> Preparing: delete from purchase where id = ?
DEBUG [main] - ==> Parameters: 7(Integer)
DEBUG [main] - <== Updates: 0
0
以上便是对在Mybatis中使用XML进行增删改查的实现方式的介绍。