目录
1、映射器简介
(1)什么是mapper动态代理?
在接口中有方法的返回值定义,参数的定义,方法名,在sqlMapper.xml中也对应这接口给予了赋值,这时候dao的实现类就显得多余,这是Mybatis可以帮助我们自动产生实现类,并可以调取方法得到结果,这就是Mybatis的mapper动态代理。
(2)动态代理的规范
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
相当于是你在Mapper层声明方法,在xml中去实现sql代码逻辑。
相当于是一个接口文件对应一个xml文件,而且必须名字相同。
也就是方法名和id名也必须相同才能映射到,Mapper接口开发需要遵循以下规范:
- Mapper.xml文件中的namespace与mapper接口的类路径相同。
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同。
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。
(3)如何使用动态代理
使用SqlSession的方法getMapper()让Mybatis自动生成对应接口的实现对象。
首先是DaoUtil层,它封装了一些加载配置文件、构建工厂、创建SqlSession对象等操作和关闭资源等操作。
public class DaoUtil {
private static SqlSessionFactory factory;
static {
try {
// 1.加载配置文件
// 加载 MyBatis 配置文件 mybatis-config.xml。配置文件中包含了数据库连接信息和 MyBatis 的各种设置
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
// 2.构建工厂
factory = new SqlSessionFactoryBuilder().build(stream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession() {
// 从 SqlSessionFactory 中打开一个新的 SqlSession。SqlSession 是 MyBatis 的核心接口之一,用于执行 SQL 命令、获取映射器以及管理事务。
return factory.openSession();
}
public static void closeResource(SqlSession sqlSession) {
// 释放资源
sqlSession.close();
}
}
然后是测试类中,调用DaoUtil方法来实现动态代理的使用。
public static void main(String[] args) {
SqlSession sqlSession = DaoUtil.getSqlSession();
StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> blist = stuMapper.findAllStudentAndClassOneTable(2);
for(Student s : blist) {
System.out.println(s);
}
DaoUtil.closeResource(sqlSession);
}
其中包含的方法有:
selectOne和selectList:动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用 selectList方法,如果返回单个对象则调用selectOne方法。如果是返回学生列表List则调用selectList(),如果是返回学生单个则调用selectOne()。
namespace:mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。
(4)为什么学映射器
半自动化的体现:配置 SQL 语句,体现了半自动化和灵活性。
ORM的体现:对象关系映射的实现,数据库表和POJO类的映射关系。
(5)映射器与接口
映射器配置文件和接口绑定:配置文件名对应接口名,id属性值对应方法名。
(6)映射器的引入
文件路径:用相对路径引入映射器
<mapper resource = ”com/codeup/dao/Mapper.xml”/>
XML:用文件定位符引入映射器
<mapper url = ”file:///var/mappers/Mapper.xml”/>
包名:用包名引入映射器
<package name = ”com.codeup.dao.Mapper”/>
类注册:用类名引入映射器
<mapper class = ”com.codeup.mapper.NewsMapper”/>
(7)映射器的组成
2、select元素
(1)select元素结构
属性 | 说明 |
id | 唯一标识,接口中的方法名; |
parameterType | 参数的类型; |
resultType | 结果的类型; |
resultMap | 复杂的结果集映射关系; |
(2)示例代码
通过班级编号来查询一个学生:
StudentMapper接口封装的抽象方法:
public Student findStudentBySid(int sid);
StudentMapperXML对应的映射器XML代码中:
<select id = "findStudentByClassid" parameterType="int" resultType="student">
select * from student where classid = #{v}
</select>
这里的id就是接口抽象方法的方法名,parameterType对应的就是传进来的参数(要查找几班的学生),resultType就是返回的类型(查询到的具体学生)。
#{v}是用来处理参数占位符的。它会将传入的参数值安全地替换到 SQL 语句中,防止 SQL 注入攻击。在XML文件中,配置文件中利用 parameterType 属性设置参数类型,利用 #{}设置参数的使用及位置。在接口中,接口中,按照方法的入参方式指定参数类型和参数名称。
(3)输出pojo对象,简单参数,pojo参数
在使用 MyBatis 或 Hibernate 等 ORM 框架时,POJO 类通常表示数据库中的表记录。
在我们写XML映射器代码时会有一些逻辑的判断,但是在XML中不能用java中的常规条件符号来写,需要用替换符号来完成逻辑的判断。
如果条件有 > < >= <= 可以使用转义字符进行代替:
示例代码:
<select id="findStudentBigSid" parameterType="int" resultType="student">
select * from student where sid < #{v}
</select>
例如这段代码用<就可以实现查找小于指定sid的学生信息。
(4)多个参数传递
方法1:Map方式(不推荐 耦合度太高)
比如说现在我们有一个对学生进行查询并且以表联查的方式对应着学生班级一对一查询,再对其结果进行分页,但是我们的映射器parameterType只能传入一个参数,呢该怎么办呢,这时候就可以传入一个Map集合,把我们想要传进入的值都放在Map里面。
接口代码:
public List<Student> findStudentByClassidAndSsexLimit(Map<String, Object> map);
XML代码:
<select id="findStudentByClassidAndSsexLimit" parameterType="map" resultType="student">
select * from student where classid = #{bj} and ssex = #{xb} limit #{wz},#{bc}
</select>
这里就将map传递进入,按照map传进去的不同的类型,对应着完成sql语句的拼接。但是如果表结构发生变化,或者参数名称改变都会要重新维护代码,所以耦合度很高。
方法2:JavaBean方式(推荐,但只能查看实体类所有属性值)
如果你要通过班级id和性别来查看一个学生的信息,呢我们完全可以实例化一个学生对象,通过给里面的参数赋值来传入映射其中,完成查询。
接口代码:
public List<Student> findStudentByClassidAndSsex(Student s);
XML代码:
<select id = "findStudentByClassidAndSsex" parameterType="student" resultType="student">
select * from student where classid = #{classid} and ssex = #{ssex}
</select>
方法3:param数 (推荐,Map和key固定)
可以通过param数来进行传递多参,param相当于Map但是它的key是固定的,值不是固定的,key从1开始往后递增。
接口代码:
public List<Student> findStudentByClassidAndSsexlimitParam(int classid, String sex, int weizhi, int buchang);
XML代码:
<select id="findStudentByClassidAndSsexlimitParam">
select * from student where classid = #{param1} and ssex = #{param2} limit #{param3},#{param4}
</select>
方法4:arg数(不推荐)
可以通过arg数来进行传递多参,从0开始依次递增。
接口代码:
public List<Student> findStudentByClassidAndSsexlimitArg(int classid, String sex, int weizhi, int buchang);
XML代码:
<select id="findStudentByClassidAndSsexlimitArg" resultType="student">
select * from student where classid = #{arg0} and ssex = #{arg1} limit #{arg2},#{arg3}
</select>
3、insert元素
(1)insert元素结构
属性 | 说明 |
id | 唯一标识,接口中的方法名; |
parameterType | 参数的类型; |
keyProperty | 表示以哪个列作为属性的主键,不能和 keyColumn 同时使用; |
keyColumn | 指明哪一列是主键,不能和 keyProperty同时使用; |
useGeneratedKeys | 使用 JDBC 的 getGeneratedKeys 方式来取有数据库内部生成的主键; |
(2)主键回填
主键回填就是当主键在数据库中为自增字段时,新增成功后,回填主键。相当于有把主键值返回给了当前的实体类对象。
两个属性:useGeneratedKeys="true" 开启主键回填,keyProperty="sid" 回填的主键给哪个属性。
自定义主键生成规则:
<selectKey>:用来预先设定主键值。自定义主键生成规则时,可以使用该标签;
order属性:取值 BEFORE,AFTER ;
4、update元素和delete元素
(1)update元素
属性 | 说明 |
id | 唯一标识,接口中的方法名; |
parameterType | 参数的类型; |
(2)delete元素
属性 | 说明 |
id | 唯一标识,接口中的方法名; |
parameterType | 参数的类型; |
5、resultMap元素
当我们给数据库中的表字段创建实体类时,因为java和数据库的命名规范不同,所以有时候可能会导致字段名和属性不一致的问题。
其中属性smname和字段名sm_name不一致,会导致映射不上的问题。
(1)为什么要使用resultMap元素
定义映射规则:ORM的特性,POJO 类和数据库的映射关系;
级联操作:多表存在主外键关系时,主表和从表之间的关联操作;
类型转换:数据库字段的类型和 POJO类属性的类型转换。
(2)resultMap元素的结构
(3)使用POJO存储结果集
属性 | 说明 |
property | 映射到列结果的字段或者属性,通常是指 POJO 的属性; |
column | 对应的数据库字段; |
javaType | Java 类型,可以是基本数据类型,也可以是类完全限定名; |
jdbcType | JDBC 的类型,基本支持所有常用的数据库类型; |
通常来讲property传入的时java中的属性,而colum代表的是对应的表中的字段名称。
如果没有做映射处理的话,POJO属性和数据库字段不对应的属性就会显示为null。
示例代码(错误):
<select id="findAllSMaster" resultType="SMaster">
select * from schoolmaster
</select>
查询结果:
查询的结果smname为null,因为属性名和字段名不对应。
示例代码(正确):
<resultMap type="smaster" id="sm_map">
<result column="sm_name" property="smname"></result>
</resultMap>
<select id="findAllSMaster" resultMap="sm_map">
select * from schoolmaster
</select>
查询结果:
6、多表联查
(1)什么是联查
级联(cascade),是指多个对象之间的映射关系,建立数据之间的级联关系提高管理效率。
一对一:一个对象对应唯一的对象,举例:中国公民和身份证;
一对多:一个对象对应多个对象,举例:班级和学生;
多对多:多个对象对应多个对象,举例:公司角色和公司员工;
(2)一对一级联步骤
(3)一对一级联映射
以一个学生为例,一个学生最多对应一个班级,所以一个学生对应一个班级,这就是一对一级联。
示例代码:
接口代码:
public List<Student> findAllStudent();
XML代码:
<resultMap type="student" id="stu_class_Map">
<result column="sid" property="sid" />
<result column="sname" property="sname" />
<result column="birthday" property="birthday" />
<result column="ssex" property="ssex" />
<result column="classid" property="classid" />
<!-- 一对一 -->
<association property="bj" javaType="Banji">
<result column="classid" property="classid" />
<result column="classname" property="classname" />
</association>
</resultMap>
<select id="findAllStudent" resultMap="stu_class_Map">
select * from student
left join class on student.classid = class.classid
</select>
这样查询就可以实现一个学生对应一个班级的查询。<association property="bj">代表的是java中属性名为bj,javaType="Banji"这个代码意思是bj的类型为Banji。这一句用于定义一对一关联关系。
(4)一对多级联步骤
(5)一对多级联
以一个班级为例,一个班级中可以有很多的学生,所以如果要以班级为主表来联查,就得在实体类中创建一个学生列表来存放学生。但是如果单独用映射器来映射的话,只能映射出班级的属性,所以要用一对多的步骤来完成对班级内的所有学生的映射操作。
示例代码:
接口代码:
public List<Banji> findAllBanjiAndStu();
XML代码:
<resultMap type="Banji" id="bj_class_Map">
<result column="classid" property="classid"/>
<result column="classname" property="classname"/>
<!-- 1对多 -->
<collection property="stulist" ofType="Student">
<result column="sid" property="sid"/>
<result column="sname" property="sname"/>
<result column="birthday" property="birthday"/>
<result column="ssex" property="ssex"/>
<result column="classid" property="classid"/>
</collection>
</resultMap>
<select id="findAllBanjiAndStu" resultMap = "bj_class_Map">
select * from class
left join student on class.classid = student.classid
</select>
这段代码property是对应java中的属性stulist学生集合,映射的对应属性是student的实体类。
(6)级联的缺陷
使用建议:
根据实际情况增加级联关系;
多层关联式,建议超过三层关联时尽量少用级联;