MyBatis-02 Sql映射文件

一、Sql映射文件顶级配置元素

  • mapper:映射文件的根元素节点,
    仅有一个属性namespace(命名空间)
    属性作用:
    (1)区分不同的mapper,全局唯一
    (2)绑定DAO接口,即面向接口 编程,当namespace绑定某一接口后,可以不用写该接口的实现类,MyBatis会通过接口的完全限定名查找到对应的mapper 配置来执行sql语句,因此namespace的命名必须要跟接口同名
  • cache:配置给定命名空间的缓存
  • cache-ref:从其他命名空间引用缓存配置
  • resultMap:用来描述数据库结果集和对象的对应关系
  • sql:可重用的sql块,也可以被其他语句引用
  • insert/delete/update/select:映射增删改查语句

注: 关于MyBatis的sql映射文件中的mapper元素的namespace属性

  • namespace的命名必须跟某个DAO接口同名,且同属于DAO层,故代码结构中,映射文件与该DAO
    接口在同一package下,并且习惯上都是以Mapper结尾,如(UserMapper.xml、UserMapper.java)
  • 在不同的mapper文件中,子元素的id可以相同,MyBatis通过namespace和子元素的id联合区分。
    接口中的方法(名)与映射文件中的sql语句id应一一对应。

二、select查询操作

注:以下代码中主要显示sql映射文件中语句,其他环境配置等代码见初始MyBatis-1

1、使用select 完成单条件查询操作
UserMapper.xml



UserMapper.java

	/**
	 * 根据用户名模糊查询用户列表
	 * @param userName 用户姓名
	 * @return	User列表
	 * @throws Exception
	 */
	public List getUserListByUserName(String userName)throws Exception;

2、使用select完成多条件传操作

  • 封装为对象入参

UserMapper.xml

	

UserMapper.java

	/**
	 * 查询用户列表(对象入参)
	 * @param user
	 * @return
	 * @throws Exception
	 */
	public List getUserList(User user)throws Exception;
  • 封装为Map入参

UserMapper.xml

	
	

UserMapper.java

	/**
	 * 查询用户列表(map入参)
	 * @param user
	 * @return
	 * @throws Exception
	 */
	public List getUserListByMap(Map userMap)throws Exception;

3、使用resultMap完成查询结果的展现

需求: 查询所有用户列表,其中的角色信息要以角色名称(userRoleName)展示,而User表中只有userId属性且与Role(角色表)主外键关系对应。

解决方案:

  • 方案一 修改pojo:User.java类,增加userRoleName属性,并修改sql映射文件UserMapper.xml的sql语句,对用户表和Role角色表进行连表查询,做resultType自动映射。
  • 方案二 通过resultMap来映射自定义结果
    注:方案一不做演示,推荐使用方式为第二种

UserMapper.java不做更改

User.java

添加属性userRoleName

	private String userRoleName;	//角色名称
	public String getUserRoleName() {
		return userRoleName;
	}
	public void setUserRoleName(String userRoleName) {
		this.userRoleName = userRoleName;
	}

UserMapper.xml

 
	
		
		
		
		
		
		
		
		
	 

UserMapperTest.java测试类

	/**
   	* 查询用户列表
   	*/
   	@Test
   	public void testGetUserList() {
   		SqlSession sqlSession = null;
   		List ulist = null;
   		try {
   			sqlSession = MyBatisUtil.createSqlSession();
   			// 对象入参
   			User user = new User();
   			user.setUserName("赵");
   			user.setUserRole(3);
   			ulist = sqlSession.getMapper(UserMapper.class).getUserList(user);
   			for (User u : ulist) {
   				 logger.info("用户编码:"+u.getUserCode()+",用户姓名:"+u.getUserName()+",用户角色:"+u.getUserRoleName()+",用户地址:"+u.getAddress());
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			MyBatisUtil.closeSqlSession(sqlSession);
    		}
    
    	}

讲解:

  • resultMap元素的属性值和子节点
    id属性 唯一标识,此id值用于select元素resultMap属性的引用
    type属性 表示该resultMap的映射结果类型
    result子节点 用于标识一些简单属性,
    result节点的属性:1、column属性表示从数据库中查询的字段名
    2、property表示查询出来的字段值赋给实体对象的哪个属性

  • MyBatis中在对查询进行select映射时的两种方式resultType和resultMap之间的关联与区别

    区别:

    • resultType直接表示返回类型,包括基础数据类型和复杂数据类型。
    • resultMap则是对外部resultMap定义的引用,对应外部resultMap的id,表示返回结果映射到哪一个resultMap上。应用场景:数据库字段信息和对象属性不一致或需要做复杂的联合查询。

    关联:
    在使用MyBatis进行查询映射时,实际上查询出的每个字段信息都放在一个对应的map里面,key对象字段名,value对应相应的值,当select元素提供的返回类型属性为resultType时,MyBatis会将map中的键值对取出赋给resultType所指定的对象的属性(即调用对象属性的setter方法)。
    因此,当使用resultType,直接在后台就能接收到相应的对象属性值。由此可看出,其实MyBatis的每个查询映射的返回值类型都是resultMap,只是当提供的返回值类型是resultType时,MyBatis会自动把对应的值赋给指定对象的属性,而当返回值类型为resultMap时,因为map不能很好的表示领域模型,所以需要手动转化为对应的实体对象。( 领域模型:业务对象模型[对象])

注:

  • 在MayBatis的select元素中,resultType与resultMap本质是一样的,都是Map数据结构,
    但resultType和resultMap属性绝对不能同时存在,只可二选一
  • resultMap的自动映射级别:使用resultMap自动映射的前提是属性名和自动名必须一致,在默认映射级别(PARTIAL)情况下,如果没有做匹配也可以在后台获取到未匹配过的属性值;若不一致且在resultMap中没有做映射,则无法获取到数据

三、增删改操作

1、insert插入操作

UserMapper.java

	/**
	 * 添加用户
	 * @param user
	 * @return
	 */
	public int add(User user)throws Exception;

UserMapper.xml

	
	
		insert into
		smbms_user(userCode,userName,userPassword,gender,birthday,phone,
		address,userRole,createdBy,creationDate)
		values(#{userCode},#{userName},#{userPassword},#{gender},#{birthday},
		#{phone},#{address},#{userRole},#{createdBy},#{creationDate})
	

2、update更新操作
UserMapper.java

	/**
	 * 修改用户
	 * @param user
	 * @return
	 */
	public int modify(User user)throws Exception;

UserMapper.xml

	
	
		UPDATE smbms_user set userCode=#{userCode},
		userName=#{userName},userPassword=#{userPassword},gender=#{gender},
		birthday=#{birthday},phone=#{phone},address=#{address},
		userRole=#{userRole},modifyBy=#{modifyBy},modifyDate=#{modifyDate}
		WHERE id=#{id} 
	

3、使用@Param注解实现多参数注入
需求: 修改指定用户的个人密码
分析: 可以明确方法传入的参数有两个,用户id和新密码,需要修改列只有一个,所以将条件参数封装为对象不合适,所以此处运用@Param实现多参数传递,代码如下
UserMapper.java

	/**
	 * 为指定用户修改密码
	 * @param id
	 * @param pwd
	 * @return
	 */
	public int updatePwd(@Param("id")Integer id,@Param("userPassword")String pwd)throws Exception;

UserMapper.xml

	
	
		update smbms_user set userPassword=#{userPassword}
		where id=#{id}
	

UserMapperTest.java测试类

	/**
	 * 修改密码
	 */
	@Test
	public void testUpdatePwd() {
		SqlSession sqlSession = null;
		int count = 0;
		try {
			sqlSession = MyBatisUtil.createSqlSession();
			count = sqlSession.getMapper(UserMapper.class).updatePwd(16, "888888");
			// int i=2/0; //模拟操作失败
			sqlSession.commit();	//提交
		} catch (Exception e) {
			sqlSession.rollback();	//回滚
			e.printStackTrace();
			count = 0;
		} finally {
			MyBatisUtil.closeSqlSession(sqlSession);
		}
		logger.debug("updatePwd count---->" + count);
	}

讲解:

  • 使用@Param与不使用的区别
    若不使用@Param注解,则会报错,报错信息类似于Parameter’参数名’
    not found。深入MyBatis源码,MyBatis的参数类型为Map,若使用 @Param注解参数,那么就会记录指定的参数名(@Param后括号内的名称)为key;若参数没有加@Param,那么就会记录"param"+它的序号作为Map的key,故在进行多参数入参时,若没有使用@Param指定参数,那么映射的sql语句中获取不到#{参数名},从而报错。

  • @Param注解参数使用场景
    (1)一般情况下,超过4个以上的参数最好封装为对象(特别是常规的增加、修改操作,字段较多的情况)
    (2)对于参数固定,推荐使用@Param,原因是此方法较灵活,代码可读性高

注: 当参数为基础数据类型时,不管是多参数入参还是单独一个参 数,都需要使用@Param注解参数

4、delete删除操作

UserMapper.java

	/**
	 * 删除指定用户
	 * @param id
	 * @return
	 */
	public int deleteUserById(@Param("id")Integer id)throws Exception;

UserMapper.xml

	
	
		delete from smbms_user
		where id=#{id}
	

四、实现MyBatis高级结果映射

1、association(一对一):
映射到某个JavaBean的某个复杂类型数据,比如Java类,即JavaBean内部嵌套一个复杂数据类型的属性,这种情况就属于复杂类型的关联。注:association仅处理一对一的关联关系。
属性

  • javaType:完整java类名或别名,若映射到一个JavaBean,则MyBatis通常会自行监测到其类型;若映射到一个HashMap,则应该明确指定javaType,来确保其行为
  • property:映射数据库列的实体对象的属性名子元素
    property子元素
    1、id:对应数据库中改行的主键id
    2、result映射数据库列
    result属性
    1)property:映射数据列的实体对象的属性
    2)column:数据库列名或别名

注: 在做映射的过程中,要确保所有的列名都是唯一且无歧义的

Demo演示
需求: 根据角色id获取用户列表

User.java添加属性Role对象

	private Role role;	//角色
	public Role getRole() {
		return role;
	}
	public void setRole(Role role) {
		this.role = role;
	}

Role.java

package cn.smbms.pojo;
import java.util.Date;

/**
 * 用户角色类
 * @author 14062
 *
 */
public class Role {
	private Integer id;			//主键id
	private String roleCode;		//角色编码
	private String roleName;		//角色名称
	private Integer createdBy;	//创建者
	private Date creationDate;	//创建日期
	private Integer modifyBy;	//修改者
	private Date modifyDate;	//修改日期
	//省略getter/setter方法	
}

UserMapper.java

	/**
	 * 根据roleId查询用户列表
	 * @param roleId
	 * @return
	 */
	public List getUserListByRoleId(@Param("userRole")Integer roleId)throws Exception;

UserMapper.xml

	
	
	
		
		
		
		
		
		
		
		
		
		
	

	
	
		
		
		
		
	

UserMapperTest.java测试类

	/**
	 * 根据角色id获取用户列表
	 */
	@Test
	public void testGetUserListByRoleId() {
		SqlSession sqlSession = null;
		List list = new ArrayList();
		try {
			sqlSession = MyBatisUtil.createSqlSession();
			list = sqlSession.getMapper(UserMapper.class).getUserListByRoleId(2);
			logger.info("符合条件的用户数:" + list.size());
			for (User user : list) {
				logger.info("用户姓名:" + user.getUserName());
				logger.info("用户角色:" + user.getRole().getId());
				logger.info("用户角色:" + user.getRole().getRoleName());
				logger.info("角色编码:" + user.getRole().getRoleCode());
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			MyBatisUtil.closeSqlSession(sqlSession);
		}
	}

2、collection(一对多):
作用与association类似,也是映射到JavaBean的某个复杂数据类型的 属性,区别是这个类型是一个集合列表,即JavaBean内部嵌套一个复杂数据类型(集合)属性。
属性

  • ofType:完整java类名或别名,即集合所包含的类型
  • property:映射实体对象中的(集合类型)属性名

例:
注: 以上代码可以理解为一个属性名为adressList,类型为Address的 集合

Demo演示

需求: 获取指定用户的相关信息和地址列表
分析: 一个用户有多个地址,用户对地址为一对多的关系
User.java添加属性List

地址集合

	private List
addressList; //用户地址列表 public List
getAddressList() { return addressList; } public void setAddressList(List
addressList) { this.addressList = addressList; }

UserMapper.java

	/**
	 * 获取指定用户的相关信息和地址列表
	 */
	public List getAddressListByUserId(@Param("id")Integer userId);

UserMapper.xml

	
	
	
		
		
		
		
		
	

	
	
		
		
		
		
		
	

UserMapperTest.java

	/**
	 * 根据用户id获取地址列表-测试
	 */
	@Test
	public void testGetAddressListByUserId() {

		SqlSession sqlSession = null;
		List list = new ArrayList();
		try {
			sqlSession = MyBatisUtil.createSqlSession();
			list = sqlSession.getMapper(UserMapper.class).getAddressListByUserId(2);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			MyBatisUtil.closeSqlSession(sqlSession);
		}
		logger.info("用户数量:" + list.size());
		if (list.size() > 0) {
			for (User u : list) {
				logger.info("用户姓名:" + u.getUserName());
				logger.info("用户密码:"+u.getUserPassword());
				logger.info("--------------地址列表-----------------");
				for (Address a : u.getAddressList()) {
					logger.info("用户id:"+a.getUserId());
					logger.info("联系人:" + a.getContact());
					logger.info("地址详情:" + a.getAddressDesc());
					logger.info("邮政编码:" + a.getPostCode());
				}
			}
		}
	}

五、MyBatis的自动映射级别

  • NONE:禁止自动匹配
  • PARTIAL(默认):自动匹配所有属性,有内部嵌套的除外
  • FULL:匹配所有

六、MyBatis执行原理

mybatis运行时要先通过resources把核心配置文件也就是mybatis.xml文件加载进来,然后通过xmlConfigBulider来解析,解析完成后把结果放入configuration中,并把它作为参数传入到build()方法中,并返回一个defaultSQLSessionFactory。我们再调用openSession()方法,来获取SqlSession,在构建SqlSession的同时还需要transaction和executor用于后续执行操作。 推荐文章MyBatis运行原理

你可能感兴趣的:(MyBatis)