MyBatis单表操作
前言
在前面一小节中,介绍了MyBatis以及MyBatis的简单操作,并且简单地分析了MyBatis的工作机制,接下来这一小节,我们来学习MyBatis的单表操作,包括简单的增删改查
MyBatis单表操作
在本小节中,依然沿用上一小节所使用的环境以及资源
增加数据
在CountryMapper.xml
中加入下面插入代码
<insert id="insertCountry">
insert into
country(country_name, country_code)
values (#{countryName}, #{countryCode})
</insert>
在CountryMapper.java
中加入代码
int insertCountry(Country country);
测试代码如下
@Test
public void testInsertCountry() {
SqlSession sqlSession = sqlSessionFactory.openSession();
CountryMapper countryMapper = sqlSession.getMapper(CountryMapper.class);
Country country = new Country();
country.setCountryCode("SGP");
country.setCountryName("新加坡");
int result = countryMapper.insertCountry(country);
// 默认情况是不自动提交事务的,需要手动提交或者在配置文件中设置自动提交
sqlSession.commit();
Assert.assertEquals(1, result);
}
上面的操作基本上没有问题,但是如果我们想插入数据的时候顺便回到自增的主键,那还需要多做一点修改
CountryMapper.xml
<insert id="insertCountry" useGeneratedKeys="true" keyProperty="id">...</insert>
userGeneratedKyes="true"
表示使用数据库的自增主键,需要注意的是,只能使用一部分支持自增主键的数据库如MySQL,keyProperty=""
指定对应的属性
如果是在不支持自增主键的数据库,如Oracle,则需要采用下面的方式
<selectKey keyColumn = "主键列" resultType ="主键类型" keyProperty="id" order="BEFORE/AFTER">
SELECT SQL LIKE:
MySQL: select LAST_INSERT_ID()
Oracle: select SEQ_ID.nextval from dual
</selectKey>
在selectKey中,需要根据具体的数据库来选择对应的获取主键的操作
order="before/after"
需要根据具体的数据库来选择,有的是先生成主键再插入,有的是插入后再生成主键
上面的两种情况中,如果主键列是由多个字段组成,则多个字段之间使用,
分隔即可
在上面的插入情况中,我们是使用对象插入的,或者说,是使用一个参数插入的,在MyBatis中,如果是单个参数,可以不同做其他操作,如果是多个参数,为了在SQL中获取参数,还需要额外的一些操作,如果简单的按照上面的操作,是无法成功的,如下
<insert id="insertCountryByNameAndCode">
insert into country(country_name, country_code)
values (#{countryName}, #{countryCode})
</insert>
int insertCountryByNameAndCode(String countryName, String countryCode);
执行结果
org.apache.ibatis.exceptions.PersistenceException:
# ....
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'countryName' not found. Available parameters are [arg1, arg0, param1, param2]
可以看到,MyBatis提示我们使用arg0, arg1
或者param1, param2
这种方式,但实际上这种方式不太好,因为在SQL中无法看出插入的数据是哪个。有两种解决方案,一种是将参数封装到一个map中,key就是参数的名称,value是参数的值,但这种方式比较麻烦,而且还需要手动构造一个map对象,另一种方案是采用下面的方式
int insertCountryByNameAndCode(@Param("countryName") String countryName, @Param("countryCode") String countryCode);
也就是使用@Param("NAME")
来指定参数的名称,这种方式是比较推荐的,当然,这种方式本质也是构造一个map,只是由MyBatis来构造,不用我们自己动手。
删除数据
删除数据的情况跟插入数据是类似的,只需要在CountryMapper.xml
中加入对应的SQL,然后在CountryMapper.java
中加入对应的操作接口即可
<delete id="deleteCountryByCountryCode">
delete from country
where country_code = #{countryCode}
</delete>
int deleteCountryByCountryCode(String countryCode);
测试的操作同上,这里就不展开了
修改数据
修改数据的操作也基本同上,可以通过构造对象作为参数,也可以传入多个参数,传入多个参数则使用@Param("")
指定参数名称即可
<update id="updateCountryNameByCountryCode">
update country
set country_name = #{countryName}
where country_code = #{countryCode}
</update>
int updateCountryNameByCountryCode(Country country);
测试的操作基本同上,这里就不展开了
查询数据
查询数据是增删改查这几个操作中使用频率最高的,也是操作方式中比较丰富的一个操作,同时也是MyBatis方便性最能体现的一个部分,在查询操作中,通常数据库返回给我们的是一行行的数据,在JDBC操作中,我们需要手动将一行行的数据封装到对象中,而在MyBatis中,这些操作通常只需要指定一个resultType
或者一个resultMap
,MyBatis就会自动将其封装到对象中,这在复杂操作中比较好用。
<select id="selectCountryByCountryCode">
select id,
country_name,
country_code
from country
where country_code = #{country_code}
</select>
Country selectCountryByCountryCode(String countryCode);
当测试上面的操作时,会出现抛出下面的异常
Caused by: org.apache.ibatis.executor.ExecutorException:
A query was run and no Result Maps were found for the Mapped Statement 'mapper.CountryMapper.selectCountryByCountryCode'.
It's likely that neither a Result Type nor a Result Map was specified.
由于MyBatis从数据库中获取的是一行行的数据,它并不知道要怎么封装这些数据,所以我们需要告诉它怎么封装这些数据,方法有两个,一个是指定resultType
,这通常适用于比较简单的情况,另一个是resultMap
,适用于比较复杂的情况
所以只需要将上面的xml修改成下面的xml即可
<select id="selectCountryByCountryCode" resultType="domain.Country">
select id,
country_name,
country_code
from country
where country_code = #{country_code}
</select>
如果有在前面指定typeAlias
,则可以直接使用类名,否则就需要使用类的全限定名
当然,上面修改后不会抛出异常,但是查询出来的对象字段除了id之外,其他的全是null,Country{id=2, countryName='null', countryCode='null'}
,原因很简单啦,就是字段没对应上,country_name
和countryName
之间对不上,解决方法有两种,一种是开启MyBatis的下划线转驼峰功能
config.xml
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
这种方式适用于数据库表字段和对象字段之间均能对应上的情况,如果对应不上,则可以使用下面的方式,通过SQL的别名方式
<select id="selectCountryByCountryCode" resultType="domain.Country">
select id,
country_name as CountryName,
country_code as CountryCode
from country
where country_code = #{country_code}
</select>
通过上面的两种方式之一修改之后就没问题啦
上面我们提到,可以通过resultType
来封装结果集,也可以通过resultMap
来封装,下面介绍下resultMap
的使用
<select id="selectCountryByCountryCode" resultMap="countryMap">
select id, country_name, country_code
from country
where country_code = #{country_code}
</select>
<!--定义resultMap-->
<resultMap id="countryMap" type="domain.Country">
<!--封装属性-->
<id property="id" column="id" />
<result property="countryName" column="country_name"/>
<result property="countryCode" column="country_code"/>
</resultMap>
在resultMap
中有几个需要注意的地方
1. 尽量指定<id>
,由于是将一行行的数据对应到对象中,那么就可能出现多行数据相同的情况,而这个时候MyBatis会将其对应到同一个对象中,如果指定了id,那么直接根据id字段的值来比较,如果没有,则根据所有的字段来比较,比较一个字段总是比比较多个字段高效的嘛
2. 在resultMap
的字标签中,均可以使用javaType=""
以及jdbcType=""
这两个类型,当对象的字段跟数据库中字段出现差异的时候,就需要显式指定了,比如Date
对象对应的应该是TIMESTAMP
而不是数据库中的Date
,这个时候就需要指定jdbcType="TIMESTAMP"
了
3. resultMap中还可以嵌套collection
、association
等字标签,下一节将详细介绍这一部分内容
总结
在本小节中,我们主要学习了MyBatis的单表操作:增删改查,其中需要注意的是如果传入的时候有多个参数,则推荐使用@Param("")
指定参数名称,如果需要获取插入的数据的主键,可以根据数据库来使用userGeneratedKey=""
或者<selectKey>
来获取,在封装数据集到对象的时候,可以使用resultType
或者resultMap
,而且必须指定一种的一种,也只能使用一种,resultType
适用于比较简单的情况,resultMap
使用与比较复杂的情况,关于resultMap
的更多用法我们将在下节进行详细学习。