在开发持久层功能时,需要为对应的功能定义抽象方法,这些抽象方法应该存在于接口中,然后,在接口中添加抽象方法,
关于抽象方法的设计原则:
- 如果方法对应的功能是执行增、删、改,可以使用`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>

需要查询的结果中的列名与返回值类型中的属性名不一致时,需要自定义别名,使得查询结果中的列名与返回值类型中的属性名保持一致,如下: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注入风险,工作效率较低。