这一篇,老多代码了,我的天。我用了经典的emp表和dept表做演示。加油!打工人。(看到隔壁的铁人为了调休奋战了36小时没睡,我陷入入了沉思)
Mybatis SQL映射
在SQL映射文件中,有需要的顶级元素标签:
--cache – 该命名空间的缓存配置。
--cache-ref – 引用其它命名空间的缓存配置。
--resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
--parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
--sql – 可被其它语句引用的可重用语句块。
--insert – 映射插入语句。
--update – 映射更新语句。
--delete – 映射删除语句。
--select – 映射查询语句。
并且在每个顶级元素标签里,是能添加很多数据的。当然了,常用就那么几个
1、insert、update、delete元素
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap | 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys | (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty | 仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset )。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn | (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
遇到新增、想获得自增主键怎么办?
不要怕,`useGeneratedKeys`来帮你
<!--如果数据库支持自增可以使用这样的方式-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into user(id,uname,age,gender,hobby) values (#{id},#{uname},#{age},#{gender},#{hobby})
</insert>
<!--如果数据库不支持自增的话,也不要怕,那么可以使用如下的方式进行赋值查询-->
<insert id="insertUser2" >
<selectKey order="BEFORE" keyProperty="id" resultType="integer">
select max(id)+1 from user
</selectKey>
insert into user(id,user_name) values(#{id},#{userName})
</insert>
2、select元素
这个元素是相当的麻烦,麻烦就麻烦这个顶级元素上了。秀到飞起
1、当查询语句的时候,一般我们都会动态的将属性添加到sql语句中,在Mybatis中当然也一样了
<!--
查询语句包含单个参数的时候,而且还没有审核人,好家伙,可以嚣张了#{想写啥就写啥,随便写}
但是!这里有但是:只限定于基本数据类型,而引用数据类型就不可以了
-->
<select id="selectEmpById" resultType="com.kaisi.bean.Emp">
select * from emp where empno = #{qwer}
</select>
为什么引用数据类型就不行了呢?
/*
在Emp的接口中,我也是根据empno进行查询对象,但是我单参数传入的却是一个对象
(可以确定是引用类型对吧!)
那么在sql映射的配置文件 EmpDao.xml中对应的sql语句是否可以#{jqk或qwe}随便写?
测试就会发现,不能了,因为当参数为一个对象的时候,是根据具体的属性名来确定从对象中的那个属性来取到需要数值
*/
public Emp selectByEmp(Emp emp);
xml文件sql如下
<!--
这个时候就别嘚瑟了,老老实实写属性名挺好,而且接口还可以重载,映射的文件也能复用,一举两得,挺好的其实
-->
<select id="selectByEmp" resultType="com.kaisi.bean.Emp">
select * from emp where empno = #{empno}
</select>
在SQL映射文件中单参数验证完毕后,就需要验证多参数传递了
//接口定义,多参数
public Emp selectByBoth(Integer empno,Double sal);
<!--进行多参数查询的映射配置文件-->
<!--
当多参数的时候需要注意,形参中字段在sql映射中无法一一对应,只能用arg0或者argue
或者param0,param1 这是因为当使用多个形参的时候,会存入map集合中,而唯一Key恰恰就是
arg0,arg1....还有个map则是param0,param1.所以sql中的#{}只能填写这俩个
(在源码中有验证,小生可没有瞎说)
-->
<select id="selectByBoth" resultType="com.kaisi.bean.Emp">
select * from emp where empno=#{arg0} and sal>#{arg1}
</select>
当然了,你如果觉得上面这种arg或者param比较low,你也可以用注解的形式
public Emp selectByBoth(@Param("empno") Integer empno,@Param("sal") Double sal);
测试类以及测试结果:想用哪种用哪种,随自己的心情
参数的取值方式(插一嘴)
在xml中我们是有俩种取值方式的一种是${}一种是#{}
<!--
当使用#{}来获取值的时候会发现打印的sql语句如下:
select * from emp where empno=? and ename=?
当使用${}来获取值的时候会发现打印的sql语句如下:
select * from emp where empno=7369 and ename='SMITH'
通过刚刚的案例大家已经发现了存在的问题了,
使用#{}方式进行取值:采用的是参数预编译的方式,参数的位置使用?进行替代,不会出现sql注入的问题
使用${}方式进行取值:采用的是直接跟sql语句进行拼接的方式:也就是老早之前会出现的sql注入问题(腹黑的家伙们发送的参数:SMITH or 1=1,直接恒成立,全部读取数据)
但是${}有时候也是有用的
此处大家需要注意,如果我们的sql语句中的某些值不支持参数预编译,那么就必须要使用${}的方式来取值了
-->
<select id="selectEmpByNoAndName" resultType="com.kaisi.bean.Emp">
select * from ${t} where empno=#{empno} and ename=${ename}
</select>
自定义结果集
当出现数据库表字段与实体类的属性不不应的时候,是无法直接进行映射的,那么就需要自定义结果集了
例如:在表中的列 我全部加了d当做开头
//在Java中我定义的属性却是没有d开头的(能自动映射上才是遇到鬼了)
private Integer id;
private String name;
private Integer age;
private String gender;
那么 就需要我们自定义结果集来进行属性和列的映射关系
<mapper namespace="com.kaisi.dao.DogDao">
<!--
在使用mybatis进行查询的时候,mybatis默认会帮我们进行结果的封装,但是要求列名跟属性名称一一对应上
在实际的使用过程中,我们会发现有时候数据库中的列名跟我们类中的属性名并不是一一对应的,此时就需要起别名
起别名有两种实现方式:
1、在编写sql语句的时候添加别名
2、自定义封装结果集
在resultMap中:也有很多属性,可以进行一一对应的映射。
type:表示为哪一个javaBean对象进行对应
id:唯一标识,方便其他属性标签进行引用
-->
<resultMap id="myDog" type="com.kaisi.bean.Dog">
<id property="id" column="id"></id>
<result property="name" column="dname"></result>
<result property="age" column="dage"></result>
<result property="gender" column="dgender"></result>
</resultMap>
<select id="selectById" resultMap="myDog">
select * from dog where id=#{qweqw}
</select>
</mapper>
在resultMap标签中还有俩个特殊的子标签association和collection
现在进行代码演示,association的用法
步骤:在Emp实体类中加入dept对象属性,那么在查询当前员工信息的时候并且查询到员工的部门信息
<!--
当一个对象中存在另一个对象该如何进行对象的自定义关系映射(关联查询)
应当用association标签,用于一个对象中存放了另一个对象,一对一的关系
当然也可以用empDept中的对象点属性的方式(爱用哪种用哪种,效果一样)
-->
<!--第一种-->
<resultMap id="empDept" type="com.kaisi.bean.Emp">
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="common"></result>
<result column="deptno" property="dept.deptno"></result>
<result column="dname" property="dept.dname"></result>
<result column="loc" property="dept.loc"></result>
</resultMap>
<select id="selectEmpAndDept" resultMap="empDept">
select * from emp e left join dept d on e.deptno = d.deptno where e.empno=#{empnoxx}
</select>
<!--第二种-->
<resultMap id="empDept2" type="com.kaisi.bean.Emp">
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="common"></result>
<result column="deptno" property="deptno"></result>
<association property="dept" javaType="com.kaisi.bean.Dept">
<id column="deptno" property="deptno"></id>
<result column="loc" property="loc"></result>
<result column="dname" property="dname"></result>
</association>
</resultMap>
<select id="selectEmpAndDept2" resultMap="empDept2">
select * from emp left join dept on emp.deptno=dept.deptno where emp.empno=#{empno}
</select>
呼呼,下面开始collocation标签的用法
这里的步骤:在Dept表中加入Emp对象属性,采用一对多的关系,查询部门下所有员工信息以及部门信息
<!--
开始进行一对多的表与属性映射:也就是将从数据库中查到的数据一一映射到实体类中
唉西;这种一对多,演示中,一个部门会有多个员工,那么就需要另一个标签,collection。用于
在collection标签中,有一个ofType属性:指定集合中的元素类型
-->
<resultMap id="DeptEmp" type="com.kaisi.bean.Dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<collection property="emps" ofType="com.kaisi.bean.Emp">
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="common"></result>
<result column="deptno" property="deptno"></result>
</collection>
</resultMap>
<select id="selectDeptAndEmp" resultMap="DeptEmp">
select * from emp left join dept on dept.deptno=emp.deptno where dept.deptno=#{deptno}
</select>
分步查询
和关联查询结果一样,但是更加灵活。这给大家写个Mybatis中分步查询的案例
老规矩:需要以下食材:
1、实体类俩份,一份Emp,一份Dept
2、全局配置xml文件一份(上一篇有写)
3、接口俩个EmpDao和DeptDao
4、sql配置文件俩 EmpDao.xml文件和DeptDao.xml文件
起锅烧油:开始
菜系:查询Emp表中某员工信息并且附带此员工的部门信息
一:
在EmpDao接口中
//我先查出这个员工信息,因为员工信息是有部门编号的
public Emp selectByStep(Integer empno);
在DeptDao接口中
//既然是分步,那么这个dept的部门编号是从员工信息中获取的才是
public Dept selectDeptByStep(Integer deptno);
DeptDao.xml文件配置如下
<!--用来分步查询使用-->
<select id="selectDeptByStep" resultType="com.kaisi.bean.Dept">
select * from dept where deptno=#{deptno}
</select>
EmpDap.xml文件配置如下
<resultMap id="empStep" type="com.kaisi.bean.Emp">
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="comm"></result>
<result column="deptno" property="deptno"></result>
<association property="dept" select="com.kaisi.dao.DeptDao.selectDeptByStep" column="deptno"></association>
</resultMap>
<select id="selectByStep" resultMap="empStep">
select * from emp where empno=#{empno}
</select>
从下面结果就可以看出,我调用了一个接口方法,但是sql语句执行了俩条,并且把结果都组合成新的结果集并且返回。
这就是分步查询:好处:海量数据下表关联查询效率会降低,分步查询效率此时会高于表连接查询
但是在表中数据量比较小的情况下,关联查询和分步查询的效率就没有很大的区别了;
但是分步查询有个好处,就是可以延迟查询
延迟查询
延迟查询的开启只需要再全局配置文件中加入
<settings>
<!--开启驼峰标识验证-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--配置懒加载开关-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
分析结果:当开启懒加载后,我们在调用的时候只是使用了dept表的中的dname属性,并不需要emp表中的数据,那么减少数据库压力,当然是越少执行越好,这就是懒加载的好处。
下面我把懒加载关闭,再看结果
<settings>
<!--开启驼峰标识验证-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--配置懒加载开关-->
<setting name="lazyLoadingEnabled" value="false"/>
</settings>
相同的调用,关闭懒加载却执行了分步查询的所有sql(是基于刚刚分步的基础上做的,俩俩配合使用奇妙的味道)