天天看点

MyBatis持久层Mapper配置

在开发持久层功能时,需要为对应的功能定义抽象方法,这些抽象方法应该存在于接口中,然后,在接口中添加抽象方法,

关于抽象方法的设计原则:

  • 如果方法对应的功能是执行增、删、改,可以使用`Integer`作为返回值类型,表示“受影响的行数”,如果不关心返回值,也可以使用`void`,如果方法对应的功能是查询,返回值可以根据实际使用需求来设计;
  • 方法的名称可以自定义,但绝不可以重载;
  • 方法的参数按需设计;

注意:当方法的参数有多个时,直接声明多个参数会出现匹配不到,解决办法:可以将这些参数封装为一个类的属性,提供get set方法,然后以这个类的对象为参数即可,还可以在每个参数前添加@Param("username")来定义所要匹配的mapper.xml配置文件中预定义的参数名 

package cn.tedu.mybatis;

public interface UserMapper {
    Integer countAll();
    //多个参数需要使用@Param注解来区别
    Integer addnew(@Param("username") String username,@Param("password") String password);
}
           
<?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="cn.tedu.mybatis.UserMapper" >
    <!--配置接口中的方法 id:方法名 resultType:返回值类型,select语句必须有此属性-->
    <!--若返回值时一个List集合,则resultType值为集合中所存元素类型-->
    <select id="countAll" resultType="java.lang.Integer">
		select count(*) from t_user
	</select>

    <insert id="addnew">
     <!--#{}表达式内的名字必须与方法中定义的@Param注解中的值相同-->
     <!--若参数是一个自定义对象,则表达式内的名字必须与对象属性的名字相同-->
     insert into t_user(username,password) values(#{username},#{password})
    </insert>		
</mapper>
           

实际开发中应该为每张表创建一个对应的实体类,该类封装表中的所有字段作为属性

查询一张表中的一条数据时,返回值可以是一个该表对应的实体类;

查询一张表中的多条数据时,返回值可以是一个该表对应的实体类集合;

关联表查询时,返回值可以设计一个VO类,该类属性与查询结果对应;

一对多关联查询时(A表一条结果对应B表多条结果),返回值可以设计一个VO类,该类属性与查询结果对应,其中多条结果可以设置为对应的实体类集合(B表对应类的集合);

在这种情况下,需要自定义<resultMap>节点,用于指导MyBatis如何将结果进行封装:

<!-- id:自定义名称 -->
<!-- type:返回结果的类型 -->
<resultMap id="DepartmentVO_Map" type="cn.tedu.mybatis.DepartmentVO">
	<!-- id节点:用于配置主键 -->
	<!-- result节点:用于配置非主键 -->
	<!-- column:查询结果的列名 -->
	<!-- property:返回结果类型中的属性名 -->
	<!-- 无论哪个节点,都是用于告之MyBatis将查询结果中哪一列的数据放到返回类型中的哪个属性中-->
	<id column="id" property="id"/>
	<result column="name" property="name"/>
	<!-- collection节点:用于配置1对多关系 -->
	<!-- ofType:集合中的元素类型 -->
	<collection property="users" ofType="cn.tedu.mybatis.User">
		<id column="uid" property="id"/>
		<result column="username" property="username"/>
		<result column="password" property="password"/>
		<result column="age" property="age"/>
		<result column="phone" property="phone"/>
		<result column="email" property="email"/>
		<result column="is_delete" property="isDelete"/>
	</collection>
</resultMap>

<select id="findById"
		resultMap="DepartmentVO_Map">
		SELECT 
			t_department.id, name,
			t_user.id AS uid, username,
			password, age,
			phone, email,
			is_delete
		FROM
			t_department
		LEFT JOIN
			t_user
		ON
			t_department.id=t_user.department_id
		WHERE
			t_department.id=#{id}
	</select>
           
MyBatis持久层Mapper配置

需要查询的结果中的列名与返回值类型中的属性名不一致时,需要自定义别名,使得查询结果中的列名与返回值类型中的属性名保持一致,如下:User类中的属性名为isDelete

<select id="findById" resultType="cn.tedu.mybatis.User">
	SELECT 
		id,username,
		password,age,
		phone,email,
		is_delete AS isDelete
	FROM 
		t_user 
	WHERE 
		id=#{id}
</select>
           

动态SQL

动态SQL指的是可以在配置SQL语句添加一些特殊的标签,例如`<if>`、`<foreach>`等,可以根据参数的不同,最终生成不同的SQL语句,则称之为动态SQL。

例如:根据若干个id删除数据,大致的SQL语句是:delete from t_user where id in (1,3,5,7,9)

在实际应用中,以上`IN`关键字后侧的括号中的值是不确定的,不光是值本身,值的数量也是不确定,并且各值之间需要使用逗号进行分隔,当值的数量不确定时,逗号的数量也是无法确定的!

在设计参数时,可以使用`List`集合类型,也可以使用数组类型:Integer deleteByIds(List<Integer> ids);

<delete id="deleteByIds">
		DELETE FROM t_user
		WHERE id IN (
			<foreach collection="list"
				item="id" separator=",">
				#{id}
			</foreach>
		)
	</delete>

<!--
collection:需要被遍历的集合或数据,
    如果抽象方法只有1个参数时,如果参数的类型是`List`集合,则取值为list,
    如果参数类型是数组,则取值为array;
    如果抽象方法有多个参数,则该属性取值为@Param("xx")注解中使用的名称。

item:遍历过程中,集合中的元素的名称,可以使用#{item值}表示被遍历到的元素的值。

separator:分隔符。

open和close:遍历生成的SQL语句部分的最左侧字符和最右侧字符。
-->
           

#{}与${}占位符

在MyBatis中,配置SQL语句时,可以使用#{}与${}这2种占位符。

#{}:可以用于占位某些值,也就是在SQL中写值的位置,都可以使用这种占位符(此前在学习JDBC时使用?的位置);

${}:可以表示SQL语句的任何部分!

在使用`#{}`对某个值进行占位时,框架对整个SQL语句是有预编译处理的,无需考虑该值的数据类型的问题;而使用`${}`占位时,框架的处理方式其实就是非常单纯的字符串拼接,需要考虑数据类型的问题,如果占位的值中包括字符串类型的值,则必须使用`''`引号!

由于`#{}`只能对某个值进行占位,SQL语句本身是相对固定的,所以,这种做法实现的功能的局限性就非常明显,由于是预编译的,没有SQL注入风险,且工作效率较高!而`${}`可以随意占位,功能可以非常灵活,但是,不是预编译的,有SQL注入风险,工作效率较低。

继续阅读