Mybatis(5)-映射文件02

目录

源码分析,mybaits如何处理参数

参数处理-$和#取值的区别

select_返回List

自定义结果映射规则-resultMap

resultMap-层联查询

         创建dept表,添加外键

        修改Emp类,新建dept

        接口类和映射文件修改

        测试结果

        注:

resultMap-关联查询-assoiation定义关联对象封装规则


源码分析,mybaits如何处理参数

========================结合源码,mybatis怎么处理参数==========================
Debug : Employee employee = mapper.getEmpByIdAndLastName(7, "wangzhaojun"); 进入方法:进入代理类MapperProxy->MapperMethod->ParamNameResolver

总结:参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key;
#{key}就可以取出map中的值;

(@Param("id")Integer id,@Param("lastName")String lastName);

参数args:Object[] args = [7,"wangzhaojun"]

ParamNameResolver解析参数封装map的;
//1、names:{0=id, 1=lastName};构造器的时候就确定好了

	确定流程:
	1.获取每个标了param注解的参数的@Param的值:id,lastName;  赋值给name;
	2.每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)
		name的值:
			标注了param注解:注解的值
			没有标注:
				1.全局配置:useActualParamName(jdk1.8):name=参数名
				2.name=map.size();相当于当前元素的索引
	{0=id, 1=lastName,2=2}
				

args【1,"Tom",'hello'】:

public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    //1、参数为null直接返回
    if (args == null || paramCount == 0) {
      return null;
     
    //2、如果只有一个元素,并且没有Param注解;args[0]:单个参数直接返回
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
      
    //3、多个元素或者有Param标注
    } else {
      final Map<String, Object> param = new ParamMap<Object>();
      int i = 0;
      
      //4、遍历names集合;{0=id, 1=lastName,2=2}  args:Object[] args = [7,"wangzhaojun"]
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
      
      	//names集合的value作为key;  names集合的key又作为取值的参考args[0]:args【1,"Tom"】:
      	//eg:{id=args[0]:1,lastName=args[1]:Tom,2=args[2]}
        param.put(entry.getValue(), args[entry.getKey()]);
        
        
        // add generic param names (param1, param2, ...)param
        //额外的将每一个参数也保存到map中,使用新的key:param1...paramN
        //效果:有Param注解可以#{指定的key},或者#{param1}
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
}
返回的param
0 = {HashMap$Node@1722} "lastName" -> "wangzhaojun"
1 = {HashMap$Node@1723} "id" -> "7"
2 = {HashMap$Node@1724} "param1" -> "7"
3 = {HashMap$Node@1725} "param2" -> "wangzhaojun"

总流程:
1.先判断是什么方法 select update等 其他update 直接
 case INSERT:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
2.如果是select 有多项判断有无返回值,是多个还是单个返回值 最后再调用
然后拼接参数 param 供给下面方法使用。
result = sqlSession.selectOne(this.command.getName(), param);

参数处理-$和#取值的区别

#{}:可以获取map中的值或者pojo对象属性的值;
${}:可以获取map中的值或者pojo对象属性的值;

select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * from tbl_employee where id=2 and last_name=?
	区别:
		#{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
		${}:取出的值直接拼装在sql语句中;会有安全问题;
		大多情况下,我们去参数的值都应该去使用#{};
		
		原生jdbc不支持占位符的地方我们就可以使用${}进行取值 , 不支持预编译的。
		比如分表、排序。。。;按照年份分表拆分
			select * from ${year}_salary where xxx;
			select * from tbl_employee order by ${f_name} ${order}

#{}:更丰富的用法:
	规定参数的一些规则:
	javaType、 jdbcType、 mode(存储过程)、 numericScale、
	resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);

	jdbcType通常需要在某种特定的条件下被设置:
		在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
		比如在插入一条数据,其中一个字段的值为null。就会报错。

		JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;
		
		由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法
		1、#{email,jdbcType=OTHER};
		2、jdbcTypeForNull=NULL
			<setting name="jdbcTypeForNull" value="NULL"/>

select_返回List

Select元素来定义查询操作。
• Id:唯一标识符。                   – 用来引用这条语句,需要和接口的方法名一致
parameterType:参数类型。 – 可以不传,MyBatis会根据TypeHandler自动推断
resultType:返回值类型。     – 别名或者全类名,如果返回的是集合,定义集合中元
素的类型。不能和resultMap同时使用。
第一种情况:
<!-- public List<Employee> getEmpsByLastNameLike(String lastName); -->

<!--resultType:如果返回的是一个集合,要写集合中元素的类型  -->
<select id="getEmpsByLastNameLike" resultType="com.atguigu.mybatis.bean.Employee">
	select * from tbl_employee where last_name like #{lastName}
</select>

第二种情况:
//多条记录封装一个map:Map<Integer,Employee>:键是这条记录的主键,值是记录封装后的javaBean
//@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key

@MapKey("lastName")
public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);

<select id="getEmpByLastNameLikeReturnMap" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where last_name like #{lastName}
</select>

第二种情况中的resultType指定的是每个记录的封装的返回类型。

自定义结果映射规则-resultMap

        1、全局setting设置
        – autoMappingBehavior默认是PARTIAL ,开启自动映射 的功能。唯一的要求是列名和javaBean属性名一致。
        – 如果autoMappingBehavior设置为null则会取消自动映射
        – 数据库字段命名规范,POJO属性符合驼峰命名法,如 A_COLUMN-> aColumn,我们可以 开启自动驼峰命名规 则映射功能 mapUnderscoreToCamelCase=true。
        也可以通过起别名的方式。
<!--自定义某个javaBean的封装规则
	type:自定义规则的Java类型
	id:唯一id方便引用
	  -->
	<resultMap type="com.atguigu.mybatis.bean.Employee" id="MySimpleEmp">
		<!--指定主键列的封装规则
		id定义主键会底层有优化;
		column:指定哪一列
		property:指定对应的javaBean属性
		  -->
		<id column="id" property="id"/>
		<!-- 定义普通列封装规则 -->
		<result column="last_name" property="lastName"/>
		<!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上。 -->
		<result column="email" property="email"/>
		<result column="gender" property="gender"/>
	</resultMap>
	
	<!-- resultMap:自定义结果集映射规则;  -->
	<!-- public Employee getEmpById(Integer id); -->
	<select id="getEmpById"  resultMap="MySimpleEmp">
		select * from tbl_employee where id=#{id}
	</select>

resultMap-层联查询

 场景一:

查询Employee的同时查询员工对应的部门

         创建dept表,添加外键

create table tbl_dept (
id int(11) PRIMARY key AUTO_INCREMENT,
dept_name varchar(255)
)

alter table tbl_employee add column d_id int(11) 

alter table tbl_employee add constraint fk_emp_dept foreign key(d_id) references tbl_dept(id)

        修改Emp类,新建dept


@Alias("emp")
public class Employee {
	private Integer id;
	private String lastName;
	private String email;
	private String gender;
	private Department dept;
	
	public Employee() {
		super();
	}
	
	public Employee(Integer id, String lastName, String email, String gender) {
		super();
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
	}
	
	public Department getDept() {
		return dept;
	}

	public void setDept(Department dept) {
		this.dept = dept;
	}

	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", lastName=" + lastName + ", email="
				+ email + ", gender=" + gender + "]";
	}
}
public class Department {
	private Integer id;
	private String departmentName;
	private List<Employee> emps;
	
	public List<Employee> getEmps() {
		return emps;
	}
	public void setEmps(List<Employee> emps) {
		this.emps = emps;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getDepartmentName() {
		return departmentName;
	}
	public void setDepartmentName(String departmentName) {
		this.departmentName = departmentName;
	}
	@Override
	public String toString() {
		return "Department [id=" + id + ", departmentName=" + departmentName
				+ "]";
	}
}

        接口类和映射文件修改

                层联属性封装查询:

接口类 : public Employee getEmpAndDept(Integer id);
映射文件 : 
<resultMap type="emp" id="empDiff">
		<id column="id" property="id" />
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		<result column="did" property="dept.id" />
		<result column="dept_name" property="dept.departmentName"/>
</resultMap>
	
<select id="getEmpAndDept" resultMap="empDiff">
		SELECT e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,
		d.id did,d.dept_name dept_name FROM tbl_employee e,tbl_dept d
		WHERE e.d_id=d.id AND e.id=#{id}
</select>

        测试结果

                 测试报错,如下:没有找到构造方法。

SQL: SELECT e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,   d.id did,d.dept_name dept_name FROM tbl_employee e,tbl_dept d   WHERE e.d_id=d.id AND e.id=?
### Cause: org.apache.ibatis.executor.ExecutorException: No constructor found in com.atguigu.mybatis.bean.Employee matching [java.lang.Integer, java.lang.String, java.lang.String, java.lang.Integer, java.lang.Integer, java.lang.String]

在Employee类加上一下代码即可。

public Employee() {
		super();
}

        注:

注:Mybatis查询结果映射实体类必须有空构造函数!!!

提问:Mybatis查询结果映射到实体类的时候,实体类为什么必须有一个空的构造函数?

答:Mybatis框架会调用这个默认构造方法来构造实例对象,即实体类需要通过Mybatis进行动态反射生成。

反射的Class.forName("className").newInstance();需要对应的类提供一个无参构造函数。

备注:

如果在类中没有提供任何构造方法,虚拟机会自动提供默认构造方法(无参构造器),但是如果提供了其他有参数的构造方法的话,虚拟机就不再为提供默认构造方法,所以默认的构造方法不是必须的,只在有多个构造方法时才是必须的显式声明的。

resultMap-关联查询-assoiation定义关联对象封装规则

<!-- 
		使用association定义关联的单个对象的封装规则;
	 -->
	<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp2">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		
		<!--  association可以指定联合的javaBean对象
		property="dept":指定哪个属性是联合的对象
		javaType:指定这个属性对象的类型[不能省略]
		-->
		<association property="dept" javaType="com.atguigu.mybatis.bean.Department">
			<id column="did" property="id"/>
			<result column="dept_name" property="departmentName"/>
		</association>
	</resultMap>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值