MyBatis入坑之映射

203 阅读5分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

个人感觉:MyBatis对结果的映射算是比Hibernate好一些的地方,但是又时候感觉还是Hibernate的全映射也是可以的,就看你怎么使用,复杂的SQL,Hibernate也是可以搞定的。

MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录

半自动持久层框架,只能对结果集映射

为了扩展针对于CUD操作的映射,MyBatis Plus全映射(CRUD),Hibernate默认就支持全映射

image-20211101185834382.png

1.简单映射

前提:针对于单表的结果操作,结果集映射成Java对象的形式有哪些?

查询结果必须设置resultType或者resultMap

select标签中resultMap属性必须找到resultMap标签中id一致

1.1 结果映射为Map

  • 使用resultType="结果集要转换的对象"

(1)默认字段做为KEY

<!-- 记录 Resultset处理成名POJO -->
<select id="load" parameterType="int" resultType="hashmap">
  SELECT * FROM sys_user WHERE id=#{userId}
</select>
<select id="listAll" resultType="hashmap">
  SELECT * FROM sys_user
</select>

(2)自定义KEY的方式

  • 可以给字段设置别名
<select id="load" parameterType="int" resultType="hashmap">
  SELECT username userName,nick_name nickName FROM sys_user WHERE id=#{userId}
</select>
  • resultMap标签,默认自动映射(字段作为Map中的KEY)
<!-- 最难最复杂的映射标签,指定字段和属性之间的映射关系 -->
<resultMap id="mapMapper" type="map" autoMapping="true">
  <result column="username" property="userName"/>
  <result column="nick_name" property="nickName"/>
</resultMap>
<!-- resultMap属性去找resultMap标签 -->
<select id="load" parameterType="int" resultMap="mapMapper">
  SELECT * FROM sys_user WHERE id=#{userId}
</select>
<select id="listAll" resultMap="mapMapper">
  SELECT * FROM sys_user
</select>

1.2 结果映射为自定义对象

<select id="get" parameterType="int" resultType="com.hanpang.model.User">
  SELECT id userId,username userName,password pwd,nick_name,age userAge,sex userSex FROM sys_user WHERE id=#{userId}
</select>
<resultMap id="userMapper" type="com.hanpang.model.User">
  <!-- JdbcType和JavaType可以忽略不设置 -->
  <id column="id" jdbcType="INTEGER" property="userId" javaType="java.lang.Integer"/>
  <result column="username" jdbcType="VARCHAR" property="userName" javaType="java.lang.String"/>
  <result column="password" property="pwd"/>
  <result column="nick_name" property="nickName"/>
  <result column="age" property="userAge"/>
  <result column="sex" property="userSex"/>
</resultMap>
<select id="queryOne" parameterType="int" resultMap="userMapper">
  SELECT * FROM sys_user WHERE id=#{userId}
</select>
<select id="listUser" resultMap="userMapper">
  SELECT * FROM sys_user
</select>

1.3 单个值或者单列

<select id="getCount" resultType="_int">
  SELECT COUNT(*) FROM sys_user
</select>
<select id="getUserIdList" resultType="int">
  SELECT id FROM sys_user
</select>

2.高级映射的衍化

关系数据中表和表之间的关系:一对多和多对一,通过设置外键在多方

  • 一对一的关系,实际就是多方的特例索引Unique
  • 多对多的关系,实际上是通过中间表(存储外键信息),拆分成两对对一对多和多对一的关系

Java中类和类之间的七种关系:

  • 一对一单向
  • 一对一双向
  • 一对多单向
  • 多对一单向
  • 一对多和多对一双向
  • 多对多单向
  • 多对多双向

2.1 准备阶段

User.java文件

public class User {

	private Integer user_id;
	private String account;
	private String password;
	private String user_name;
	private Integer status;
	private Date login_time;
	private String ip;
	private Integer fk_role_id;
	//关联对象
	private Role role;
        
        //省略getter和setter方法
}

Role.java 文件

public class Role {

	private Integer role_id;
	private String role_name;
	private String role_key;
	private Integer status;

        //省略getter和setter方法
}

2.2 association 关联对象标签

主要针对一对一和多对一标签

2.2.1 传统方式处理

  • UserMapper.xml映射文件
<mapper namespace="com.shxt.model.User">
    <resultMap type="com.shxt.model.User" id="BaseResultMapper">
        <id column="user_id" property="user_id"/>
        <result column="account" property="account"/>
        <result column="password" property="password"/>
        <result column="user_name" property="user_name"/>
        <result column="status" property="status"/>
        <result column="login_time" property="login_time"/>
        <result column="ip" property="ip"/>
        <result column="fk_role_id" property="fk_role_id"/>
    </resultMap>
    <sql id="sys_user_columns">
        user_id,account,password,user_name,status,login_time,ip,fk_role_id
    </sql>    
    <select id="load" parameterType="_int" resultMap="BaseResultMapper">
        SELECT
            <include refid="sys_user_columns"/>
        FROM
            sys_user
        WHERE
            user_id=#{user_id}
    </select>
</mapper>
  • UserDao接口和UserDaoImpl实现类

UserDao.java接口代码

public interface UserDao {
	User getUserByPK(int user_id);
}

UserDaoImpl.java实现类

public class UserDaoImpl implements UserDao {
	@Override
	public User getUserByPK( int user_id ) {
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtils.getSqlSession();
			return sqlSession.selectOne(User.class.getName()+".load", user_id);
		} finally {
			MyBatisUtils.closeSqlSession(sqlSession);
		}
	}
}
  • RoleMapper.xml映射文件
<mapper namespace="com.shxt.model.Role">
    <resultMap type="com.shxt.model.Role" id="BaseResultMapper">
        <id column="role_id" property="role_id"/>
        <result column="role_name" property="role_name"/>
        <result column="role_key" property="role_key"/>
        <result column="status" property="status"/>
    </resultMap>
    <sql id="sys_role_columns">
        role_id,role_name,role_key,status
    </sql>
    <select id="get" parameterType="_int" resultMap="BaseResultMapper">
        SELECT
            <include refid="sys_role_columns"/>
        FROM
            sys_role
        WHERE role_id=#{role_id}
    </select>
</mapper>
  • RoleDao.java文件和RoleDaoImpl.java实现类

RoleDao.java接口

public interface RoleDao {
	Role getRoleByPK(int role_id);
}

RoleDaoImpl.java实现类

public class RoleDaoImpl implements RoleDao {
	@Override
	public Role getRoleByPK( int role_id ) {
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtils.getSqlSession();

			return sqlSession.selectOne(Role.class.getName()+".get", role_id);
		} finally {
			MyBatisUtils.closeSqlSession(sqlSession);
		}
	}
}
  • Java代码测试
    @Test
	public void 获取用户信息(){
		UserDao userDao = new UserDaoImpl();//接口回调
		User user = userDao.getUserByPK(-999);
		//获取对应外键信息
		if(user.getFk_role_id()!=null){
			RoleDao roleDao = new RoleDaoImpl();
			//查询角色对应的信息
			Role role = roleDao.getRoleByPK(user.getFk_role_id());
			//建立关系
			user.setRole(role);
		}
		System.out.println(user);
	}
  • 图解说明 一对一传统方式

2.2.2 resultMap标签之select方式

其实就是替换了

//获取对应外键信息
if(user.getFk_role_id()!=null){
  RoleDao roleDao = new RoleDaoImpl();
   //查询角色对应的信息
   Role role = roleDao.getRoleByPK(user.getFk_role_id());
   //建立关系
   user.setRole(role);
}

这段代码

  • 映射文件
     <resultMap type="com.shxt.model.User" id="BaseResultMapper">
        <id column="user_id" property="user_id"/>
        <result column="account" property="account"/>
        <result column="password" property="password"/>
        <result column="user_name" property="user_name"/>
        <result column="status" property="status"/>
        <result column="login_time" property="login_time"/>
        <result column="ip" property="ip"/>
        <result column="fk_role_id" property="fk_role_id"/>
    </resultMap>

 <resultMap type="com.shxt.model.User" id="SimpleResultMapper"  extends="BaseResultMapper">
        <!-- association 用来映射关联对象  -->
        <association property="role" javaType="com.shxt.model.Role"
        column="fk_role_id"
        select="com.shxt.model.Role.get"
        />
    </resultMap>

    <sql id="sys_user_columns">
        user_id,account,password,user_name,status,login_time,ip,fk_role_id
    </sql>
    <!-- 使用了另一个ID resultMap="SimpleResultMapper"  -->
    <select id="load" parameterType="_int" resultMap="SimpleResultMapper">
        SELECT
            <include refid="sys_user_columns"/>
        FROM
            sys_user
        WHERE
            user_id=#{user_id}
    </select>

  • Java测试代码
	@Test
	public void SELECT方式(){
		UserDao userDao = new UserDaoImpl();//接口回调
		User user = userDao.getUserByPK(-999);
		System.out.println(user);
	}
  • 图解说明 select方式说明 结果集继承

2.2.3 resultMap标签之resultMap属性方式

  • UserDao接口和UserDaoImpl实现类 UserDao.java代码
public interface UserDao {
	User getUserLeftJoin(int user_id);
}

UserDaoImpl.java代码


public class UserDaoImpl implements UserDao {
	@Override
	public User getUserLeftJoin( int user_id ) {
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtils.getSqlSession();

			return sqlSession.selectOne(User.class.getName()+".getUserLeftJoin", user_id);
		} finally {
			MyBatisUtils.closeSqlSession(sqlSession);
		}
	}
}
  • 映射文件
<mapper namespace="com.shxt.model.User">

    <sql id="sys_user_columns_alias">
        ${alias}.user_id,${alias}.account,${alias}.password,
        ${alias}.user_name,${alias}.status,${alias}.login_time,
        ${alias}.ip,${alias}.fk_role_id
    </sql>
    

     <resultMap type="com.shxt.model.User" id="BaseResultMapper">
        <id column="user_id" property="user_id"/>
        <result column="account" property="account"/>
        <result column="password" property="password"/>
        <result column="user_name" property="user_name"/>
        <result column="status" property="status"/>
        <result column="login_time" property="login_time"/>
        <result column="ip" property="ip"/>
        <result column="fk_role_id" property="fk_role_id"/>
    </resultMap>
    <resultMap type="com.shxt.model.User" 
        id="JoinResultMapper" 
        extends="BaseResultMapper">
         <association property="role" javaType="com.shxt.model.Role"
            resultMap="com.shxt.model.Role.BaseResultMapper"
        >
            <!-- <id column="role_id" property="role_id"/>
            <result column="role_name" property="role_name"/>
            <result column="role_key" property="role_key"/>
            <result column="status" property="status"/> -->
        </association>
    
    </resultMap>
    <select id="getUserLeftJoin" parameterType="_int" 
        resultMap="JoinResultMapper">
        SELECT
            <include refid="sys_user_columns_alias">
                <property name="alias" value="u"/>
            </include>
            , 
            <!-- 关键如何找到那些字段 -->
            <include refid="com.shxt.model.Role.sys_role_columns_alias">
                <property name="alias" value="r"/>
            </include>
        FROM
            sys_user u
        LEFT JOIN sys_role r ON u.fk_role_id = r.role_id
        WHERE
            u.user_id =#{user_id}
    </select>
  • Java测试代码
	@Test
	public void 连接查询_结果集处理(){
		UserDao userDao = new UserDaoImpl();//接口回调
		User user = userDao.getUserLeftJoin(-999);
		System.out.println(user);
	}
  • 图解说明 推荐说明

2.2.4 多对一映射测试

  • UserDao接口和UserDaoImpl实现类 UserDao.java代码
public interface UserDao {
	List<User> list01();
	List<User> list02();

}

UserDaoImpl.java代码

public class UserDaoImpl implements UserDao {
	@Override
	public List<User> list01() {
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtils.getSqlSession();

			return sqlSession.selectList(User.class.getName()+".list01");
		} finally {
			MyBatisUtils.closeSqlSession(sqlSession);
		}
	}

	@Override
	public List<User> list02() {
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtils.getSqlSession();

			return sqlSession.selectList(User.class.getName()+".list02");
		} finally {
			MyBatisUtils.closeSqlSession(sqlSession);
		}
	}

}
  • 映射文件
    <select id="list01" resultMap="SimpleResultMapper">
        SELECT
            <include refid="sys_user_columns"/>
        FROM
            sys_user
    </select>
    <select id="list02"  resultMap="JoinResultMapper">
        SELECT
            <include refid="sys_user_columns_alias">
                <property name="alias" value="u"/>
            </include>
            , 
            <include refid="com.shxt.model.Role.sys_role_columns_alias">
                <property name="alias" value="r"/>
            </include>
        FROM
            sys_user u
        LEFT JOIN sys_role r ON u.fk_role_id = r.role_id
    </select>
  • 图解说明 多对一测试

3 collection 关联对象标签

主要针对一对多和多对多标签

3.1 准备阶段

User.java持久化类

public class User {

	private Integer user_id;
	private String account;
	private String password;
	private String user_name;
	private Integer status;
	private Date login_time;
	private String ip;
	private Integer fk_role_id;
}

这里没有设置关联对象了

Role.java持久化类

public class Role {

	private Integer role_id;
	private String role_name;
	private String role_key;
	private Integer status;

	//关联对象
	private List<User> userList;
}

一个角色对应多个用户,我们使用集合进行处理

3.2 MyBatis的处理方式

3.2.1 collection标签之select方式

  • 映射文件说明 RoleMapper.xml代码如下:
<mapper namespace="com.shxt.model.Role">
    <!-- 基础的映射 -->
    <resultMap type="Role" id="BaseResultMapper">
        <id column="role_id" property="role_id"/>
        <result column="role_name" property="role_name"/>
        <result column="role_key" property="role_key"/>
        <result column="status" property="status"/>
    </resultMap>
    <!-- 公共代码字段提取  -->
    <sql id="sys_role_columns">
        role_id,role_name,role_key,status
    </sql>
    <sql id="sys_role_columns_alias">
        ${alias}.role_id,${alias}.role_name,${alias}.role_key,${alias}.status
    </sql>
    
    <resultMap type="Role" id="SimpleResultMapper" 
        extends="BaseResultMapper">
        
        <!-- 配置集合 List<User>-->
        <collection property="userList" 
        
        javaType="java.util.List" 
        
        ofType="com.shxt.model.User"
        
        column="role_id"
        
        select="com.shxt.model.User.listUser"
        />
    </resultMap>
    
    <select id="load" parameterType="int" resultMap="SimpleResultMapper">
        SELECT
            <include refid="sys_role_columns"/>
        FROM
            sys_role
        WHERE
            role_id=#{role_id}
    </select>
</mapper>

这里使用了<collection> 标签

<collection property="类中的属性" 
        
        javaType="类中属性对应的类型,可以省略" 
        
        ofType="结果集处理的类型,也可以理解为结合中泛型的类型,必须填写"
        
        column="通过查询的那个字段的值,在进行查询,该字段必须有查询,不然无法查找数据"
        
        select="com.shxt.model.User.listUser  命名空间.ID 使用column的值去执行对应的SQL语句"
/>

UserMapp.xml代码如下:

基本上不会对其进行改变

<mapper namespace="com.shxt.model.User">
   
     <resultMap type="com.shxt.model.User" id="BaseResultMapper">
        <id column="user_id" property="user_id"/>
        <result column="account" property="account"/>
        <result column="password" property="password"/>
        <result column="user_name" property="user_name"/>
        <result column="status" property="status"/>
        <result column="login_time" property="login_time"/>
        <result column="ip" property="ip"/>
        <result column="fk_role_id" property="fk_role_id"/>
    </resultMap>
    
    
    <sql id="sys_user_columns">
        user_id,account,password,user_name,status,login_time,ip,fk_role_id
    </sql>
    <sql id="sys_user_columns_alias">
        ${alias}.user_id,${alias}.account,${alias}.password,
        ${alias}.user_name,${alias}.status,${alias}.login_time,
        ${alias}.ip,${alias}.fk_role_id
    </sql>
    
    <select id="listUser" parameterType="int" resultMap="BaseResultMapper">
        SELECT
            <include refid="sys_user_columns"/>
        FROM
            sys_user
        WHERE
            fk_role_id=#{role_id}
    
    </select>
</mapper>
  • RoleDao接口和RoleDaoImpl实现类 RoleDao.java代码如下:
public interface RoleDao {
	Role getRoleByPK(int role_id);
}

RoleDaoImpl.java代码如下:

public class RoleDaoImpl implements RoleDao {

	@Override
	public Role getRoleByPK( int role_id ) {
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtils.getSqlSession();
			return sqlSession.selectOne(Role.class.getName()+".load", role_id);
		} finally {
			MyBatisUtils.closeSqlSession(sqlSession);
		}
	}
}
  • Java测试代码
	@Test
	public void test01(){
		RoleDao roleDao = new RoleDaoImpl();

		System.out.println(roleDao.getRoleByPK(-100));
	}
  • 图解说明

select方式图解说明

3.2.2 collection标签之resultMap属性说明

因为select方式总是执行的SQL语句比较多,我们可以通过连接查询解决问题

  • 映射文件 RoleMapper.xml代码如下:
<mapper namespace="com.shxt.model.Role">
    <resultMap type="Role" id="BaseResultMapper">
        <id column="role_id" property="role_id"/>
        <result column="role_name" property="role_name"/>
        <result column="role_key" property="role_key"/>
        <result column="status" property="status"/>
    </resultMap>
    <sql id="sys_role_columns">
        role_id,role_name,role_key,status
    </sql>
    <sql id="sys_role_columns_alias">
        ${alias}.role_id,${alias}.role_name,${alias}.role_key,${alias}.status
    </sql>
    <resultMap type="Role" id="JoinResultMapper" extends="BaseResultMapper">
        <collection property="userList" javaType="list" ofType="com.shxt.model.User"
        resultMap="com.shxt.model.User.BaseResultMapper"
        />
   
    </resultMap>
    <select id="getJoin" parameterType="int" resultMap="JoinResultMapper">
        SELECT
            <include refid="sys_role_columns_alias">
                <property name="alias" value="sr"/>
            </include>
            , 
           <include refid="com.shxt.model.User.sys_user_columns_alias">
                <property name="alias" value="su"/>
            </include>
        FROM
            sys_role sr
        LEFT JOIN sys_user su ON sr.role_id = su.fk_role_id
        WHERE
            sr.role_id =#{role_id}
    </select>
</mapper>

说明

 <collection property="userList" javaType="list" ofType="com.shxt.model.User"
        resultMap="com.shxt.model.User.BaseResultMapper 命名空间.ID找到结果集映射的位置"
  • RoleDao接口和RoleDaoImpl实现类 RoleDao.java代码
public interface RoleDao {
	Role getJoinRolrByPK(int role_id);
}

RoleDaoImpl.java代码

public class RoleDaoImpl implements RoleDao {
	@Override
	public Role getJoinRolrByPK( int role_id ) {
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtils.getSqlSession();
			return sqlSession.selectOne(Role.class.getName()+".getJoin", role_id);
		} finally {
			MyBatisUtils.closeSqlSession(sqlSession);
		}
	}
}
  • Java测试代码
	@Test
	public void test02(){
		RoleDao roleDao = new RoleDaoImpl();

		System.out.println(roleDao.getJoinRolrByPK(-100222));
	}

后话:Hibernate的多对多和MyBatis的多对多处理方案不太一样,但是个人还是比较喜欢MyBatis的方式,其实Hibernate也可以一样优秀的处理