一、XML方式
MyBatis 的真正强大之处在于它的映射语句,这也是它的魔力所在。由于它的映射语句异常强大,映射器的XML 文件就显得相对简单。如果将其与具有相同功能的JDBC 代码进行对比, 立刻就会发现,使用这种方法节省了将近95%的代码量。 MyBatis 就是针对SQL构建的,并且比普通的方法做的更好。
MyBatis 3.0 的一个最大变化,就是支持使用接口来调用方法。MyBatis 使用Java 的动态代理可以直接通过接口来调用相应的方法,不需要提供接口的实现类,更不需要在实现类中使用 SqlSession以通过命名空间间接调用。
二、xml示例:
2.1 创建数据库
创建如下的数据库,并插入数据:
CREATE TABLE `sys_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户 ID',
`user_name` varchar(50) DEFAULT NULL COMMENT '用户名',
`user_password` varchar(50) DEFAULT NULL COMMENT '密码',
`user_email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`user_info` text COMMENT '简介',
`head_img` blob COMMENT '头像',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1002 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户农';
2.2 创建实体类
创建对应sys_user数据表的实体类:
public class SysUser {
private Long id;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String userPassword;
/**
* 邮箱
*/
private String userEmail;
/**
* 简介
*/
private String userInfo;
/**
* 头像
*/
private byte[] headImg;
/**
* 创建时间
*/
private Date createTime;
public Long getId() {
return id;
}
public String getUserName() {
return userName;
}
public String getUserPassword() {
return userPassword;
}
public String getUserEmail() {
return userEmail;
}
public String getUserInfo() {
return userInfo;
}
public byte[] getHeadImg() {
return headImg;
}
public Date getCreateTime() {
return createTime;
}
public void setId(Long id) {
this.id = id;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public void setUserInfo(String userInfo) {
this.userInfo = userInfo;
}
public void setHeadImg(byte[] headImg) {
this.headImg = headImg;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
对于 SysUser 实体类,在后面使用这些对象的时候,可以通过resultMap 对数据库的列和类的宇段配置映射关系。
2.3 创建UserMapper.xml映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.wyf.mybaties.mapper.UserMapper">
<resultMap id="userMap" type="com.wyf.mybaties.model.SysUser">
<id property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="userPassword" column="user_password"/>
<result property="userEmail" column="user_email"/>
<result property="userInfo" column="user_info"/>
<result property="headImg" column="head_img" jdbcType="BLOB"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- select查询 -->
<select id="selectById" resultMap="userMap">
select * from sys_user where id = #{id}
</select>
</mapper>
需要注意的是<mapper>根标签的namespace 属性。当Mapper 接口和XML 文件关联的
时候,命名空间namespace 的值就需要配置成接口的全限定名称。
xml映射文件中标签含义:
- <select>: 映射查询语句使用的标签。
- id:命名空间中的唯一标识符,可用来代表这条语句。
- select 标签中的select * from sys_user where id= #{id}是查询语句。
- # {id} : MyBatis SQL 中使用预编译参数的一种方式,大括号中的id 是传入的参数名。
- resultMap:用于设置返回值的类型和映射关系。
resultMap 标签用于配置Java 对象的属性和查询结果列的对应关系,通过 resultMap中配置的column 和property 可以将查询列的值映射到type 对象的属性上。
2.4 创建XML文件对应的接口类
public interface UserMapper {
/**
* 通过ID查询用户
* @param id
* @return
*/
SysUser selectById(Long id);
}
接口中定义的返回值类型必须和泊位中配置的resultType 类型一致,否则就会因为类型不一致而抛出异常。 xml方式下,返回值类型是由XML 中的resultType (或 resultMap 中的type)决定的,不是由接口中写的返回值类型决定的
2.5 MyBatis主配置文件
<?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="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<package name="com.wyf.mybaties.model"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.wyf.mybaties.mapper" />
</mappers>
</configuration>
在配置文件中,我们可以直接在<mappers>指定映射文件,如下
<mappers>
<mapper resource=”com/wyf/mybaties/mapper/CountryMapper.xml ” / >
<mapper resource=”com/wyf/mybaties/mapper/UserMapper.xml ” / >
<mapper resource=”com/wyf/mybaties/mapper/RoleMapper.xml” />
<mapper resource=”com/wyf/mybaties/mapper/PrivilegeMapper.xml ” />
<mapper resource=”com/wyf/mybaties/mapper/UserRoleMapper.xml ” />
<mapper resource=”com/wyf/mybaties/mapper/RolePrivilegeMapper.xml ” />
</mappers>
这种配置方式需要将所有映射文件一一列举出来,如果增加了新的映射文件,还需要注意在此处进行配置,操作起来比较麻烦。由于此处所有的XML映射文件都有对应的Mapper 接口,所以还有一种更简单的配置方式,代码如下:
<mappers>
<package name="com.wyf.mybaties.mapper" />
</mappers>
这种配置方式会先查找包com.wyf.mybaties.mapper下所有接口,循环对接口进行如下操作:
- 判断接口对应的命名空间是否己经存在,如果存在就抛出异常,不存在就继续进行接下来的操作;
- 加载接口对应的XML映射文件, 将接口全限定名转换为路径。 例如, 将接口com.wyf.mybaties.mapper.UserMapper 转换为com/wyf/mybaties/mapper/UserMapper. xml,
然后以.xml 为后缀搜索XML 资源,如果找到就解析XML。 - 处理接口中的注解方法。
三、方法测试:
3.1 select用法
我们需要查询数据库中的数据。 在使用纯粹的 JDBC 时,需要写查询语句,并且对结果集进行手动处理, 将结果映射到对象的属性中。使用MyBatis 时,只需要在XML 中添加一个select 元素,写一个SQL, 再做一些简单的配置,就可以将查询的结果直接映射到对象中。
如上节示例,我们在xml映射文件以及对应的接口文件添加了一个根据用户 id 查询用户信息的简单方法。
前面创建接口和XML时提到过,接口和XML是通过将namespace 的值设置为接口的全限定名称来进行关联的,那么接口中方法和XML又是怎么关联的呢?
通过前文代码,我们可以发现XML 中的 select 标签的id 属性值和定义的接口方法名是一样的MyBatis ,就是通过这种方式将接口方法和XML中定义的SQL语句关联到一起的,如果接口方法没有和XML 中的id 属性值相对应,启动程序便会报错。映射XML和接口的命名需要符合如下规则:
- 当只使用XML而不使用接口的时候, namespace 的值可以设置为任意不重复的名称。
- 标签的id 属性值在任何时候都不能出现英文句号“.”,并且同一个命名空间下不能出现重复的id。
- 因为接口方法是可以重载的,所以接口中可以出现多个同名但参数不同的方法,但是XML 中 id 的值不能重复,因而接口中的所有同名方法会对应着XML 中的同一个id的方法。最常见的用法就是,同名方法中其中一个方法增加一个 RowBound 类型的参数用于实现分页查询。
测试代码如下:
/**
* 基础测试类
*/
public class BaseMapperTest {
private static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void init(){
try{
Reader reader= Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
reader.close();
}catch(IOException ex){
ex.printStackTrace();
}
}
/**
* 由SqlSessionFactory工厂对象,生产SqlSession
* @return
*/
public SqlSession getSqlSession (){
return sqlSessionFactory.openSession();
}
}
测试:
public class UserMapperTest extends BaseMapperTest{
@Test
public void TestSelectById(){
//获取SqlSession
SqlSession sqlSession = getSqlSession();
try{
//获取UserMapper接口
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用selectByid 方法,查询id = 1 的用户
SysUser user = userMapper.selectById(1l);
System.out.println(user.getCreateTime());
}finally {
sqlSession.close();
}
}
}
3.2 Insert方法
我们可以在UserMapper.xml中添加如下代码:
<!-- insert数据插入 -->
<insert id="insert">
insert into sys_user(
user_name, user_password, user_email,
user_info, head_img, create_time)
values(
#{userName}, #{userPassword}, #{userEmail},
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>
此处<insert>中的 SQL 就是一个简单的 INSERT 语句,将所有的列都列举出来, 在values 中通过#{property}方式从参数中取出属性的值。
为了防止类型错误,对于一些特殊的数据类型,建议指定具体的jdbcType值。 例如headimg 指定BLOB 类型, createTime 指定TIMESTAMP 类型。
我们在与UserMapper.xml相关联的接口中,添加对应的方法:
/**
* 新增用户
*
* @param sysUser
* @return 返回影响行数
*/
int insert(SysUser sysUser);
编写测试代码:
/**
* insert方法测试
*/
@Test
public void InsertToUser(){
//获取SqlSession
SqlSession sqlSession = getSqlSession();
try{
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
SysUser sysUser = new SysUser();
sysUser.setUserName("wyf");
sysUser.setUserPassword("111111");
sysUser.setUserEmail("wyf@book.com");
sysUser.setUserInfo("wyf info");
sysUser.setHeadImg(new byte[]{1,2,3}); //实际是一张图片
sysUser.setCreateTime(new Date());
//将新建的对象插入数据库中,返回值result 是执行的SQL 影响的行数
int res = userMapper.insert(sysUser);
System.out.println("数据插入执行结果:"+res);
}finally {
//默认的sqlSessionFactory.openSession()是不自动提交的
//因此不手动执行commit 也不会提交到数据库
sqlSession.commit(); //提交数据插入
sqlSession.close();
}
}
3.3 upDate方法
在UserMapper.xml映射文件中添加以下方法:
<!-- update 数据更新 -->
<update id="updateById">
update sys_user
set user_name= #{userName} ,
user_password= #{userPassword},
user_email = #{userEmail} ,
user_info= #{userInfo} ,
head_img = #{headImg , jdbcType=BLOB} ,
create_time = #{createTime , jdbcType=TIMESTAMP}
where id = #{id}
</update>
在UserMapper.xml映射文件对应的接口文件中添加相应的方法:
/**
* 根据主键更新
*
* @param sysUser
* @return
*/
int updateById(SysUser sysUser);
编写测试代码:
/**
* Update方法测试
*/
@Test
public void UpDateToUser(){
//获取SqlSession
SqlSession sqlSession = getSqlSession();
try{
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//从数据库查询获取一个user对象
SysUser sysUser = userMapper.selectById(1L);
//修改
sysUser.setUserName("wyf_test");
sysUser.setUserPassword("123456");
sysUser.setUserEmail("wyf_test@book.com");
//更新数据,返回影响行数
int res = userMapper.updateById(sysUser);
System.out.println("数据修改执行影响结果:"+res);
}finally {
//默认的sqlSessionFactory.openSession()是不自动提交的
//因此不手动执行commit 也不会提交到数据库
sqlSession.commit(); //提交数据插入
sqlSession.close();
}
}
3.4 delete方法
在UserMapper.xml文件中添加以下方法:
<!-- delete删除数据 -->
<delete id="deleteById">
delete from sys_user where id = #{id}
</delete>
在UserMapper.xml映射文件对应的接口文件中添加相应的方法:
/**
* 通过主键删除
*
* @param id
* @return
*/
int deleteById(Long id);
测试代码:
/**
* delete方法测试
*/
@Test
public void DeleteToUser(){
//获取SqlSession
SqlSession sqlSession = getSqlSession();
try{
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//从数据库查询获取一个user对象
SysUser sysUser = userMapper.selectById(1L);
//删除对象
int res = userMapper.deleteById(sysUser.getId());
System.out.println("数据修改执行影响结果:"+res);
}finally {
//默认的sqlSessionFactory.openSession()是不自动提交的
//因此不手动执行commit 也不会提交到数据库
sqlSession.commit(); //提交数据
sqlSession.close();
}
}
3.5 多个接口参数的用法
通过观察,不难发现目前所列举的接口中方法的参数只有一个,参数的类型可以分为两种:一种是基本类型,另一种是JavaBean。
当参数是一个基本类型的时候,它在XML文件中对应的SQL 语句只会使用一个参数,例如delete 方法。当参数是一个JavaBean 类型的时候,它在XML文件中对应的SQL语句会有多个参数,例如insert、 update 方法。
在实际应用中经常会遇到使用多个参数的情况。之前例子中,我们将多个参数合并到一个JavaBean 中,并使用这个JavaBean 作为接口方法的参数。这种方法用起来很方便,但并不适合全部的情况,因为不能只为了两三个参数去创建新的JavaBean 类,因此对于参数比较少的情况,还有两种方式可以采用:使用Map 类型作为参数或使用@Param 注解。
<!-- 多参数 -->
<select id="selectRolesByUserIdAndRoleEnabled" resultType="com.wyf.mybaties.model.SysRole">
select
r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time createTime
from sys_user u
inner join sys_user_role ur on u.id = ur.user_id
inner join sys_role r on ur.role_id = r.id
where u.id = #{userId} and r.enabled = #{enabled}
</select>
接口方法中,参数要添加注解:
/**
* 根据用户 id 和 角色的 enabled 状态获取用户的角色
*
* @param userId
* @param enabled
* @return
*/
List<SysRole> selectRolesByUserIdAndRoleEnabled(@Param("userId")Long userId, @Param("enabled")Integer enabled);
给参数配置@Param注解后, MyBatis 就会自动将参数封装成Map 类型,@Param 注解值
会作为Map 中的key,因此在SQL 部分就可以通过配置的注解值来使用参数。
四、结束
本文介绍了MyBatis使用XML的配置方式,并基于其实现了select、insert、update和delete操作。