[MyBatis] MyBatis理论入门
- 什么是MyBatis
- iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
- 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
- MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
- MyBatis特点
- 简单易学,
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
- 解除sql与程序代码的耦合:通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql。
- MyBatis执行流程
- 加载配置(xml或注解)
MyBatis将SQL的配置信息加载为一个个的MappedStatement对象(包括sqlId,sql语句,入参信息,返回值信息等),并将其存储在内存中 - SQL解析
当API接口层收到调用请求以后,会接收到传入的SQLId以及传入的入参对象(可以是任何类型),MyBatis根据SQL的Id找到对应的MappedStatement对象,然后根据传入参数对象对MappedStatement对象进行解析(将实际传入的参数来替换占位符参数,得到一条能够执行的sql语句) - SQL执行
将解析得到的sql语句发送到数据库进行执行,得到数据库操作以后的结果 - 结果集映射
将数据库操作后得到的结果根据出参的配置进行自动转换,可以转换成任意类型,最后,将结果返回给调用方 - MyBatis中的配置文件
sqlMapConfig.xml(仅有一个),放在src目录下 - <configuration></configuration>:包含配置信息
- <environments default="environment"></environments>:默认的数据库连接环境,该节点中可包含多个<environments>标签
- default:默认使用哪一个<environments>节点,值为<environments>中的id属性
- <environments></environments>:数据库连接环境,可有多个
<configuration> <!-- 数据库连接环境,default属性表示当前默认使用哪一个环境,值就是下面 environment子节点的id--> <environments default="environment"> <!--environment节点可以有多个,表示不同的环境 --> <environment id="environment"> <!-- transactionManager 必须写在最上面 --> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@192.168.3.5:1521:XE" /> <property name="username" value="SYSTEM" /> <property name="password" value="system" /> </dataSource> </environment> </environments> <!-- 包含了映射文件的信息 --> <mappers> <mapper resource="com/niit/entity/Dept.xml"/> <mapper resource="com/niit/entity/Emp.xml"/> </mappers> </configuration>
- MyBatis放置SQL语句的XML文件(映射文件)
- <mapper></mapper>:定义需要执行的一些SQL语句的信息
- namespace属性:命名空间,可以用来指定sqlId所在的xml文件,每一个xml文件都有一个唯一的namespace
- namespace属性:命名空间,可以用来指定sqlId所在的xml文件,每一个xml文件都有一个唯一的namespace
- <insert></insert>:新增。 <update></update>:更新 <select></select>:搜索 <delete></delete>:删除
- id:当前SQL语句的一个唯一标识,在同一个映射文件中id不能重复
- paramType:入参的类名
- #{ }:占位符参数
- ${ }:占位符。在添加到SQL语句中的值不会自动添加 单引号( ‘ ’)
- 动态SQL
在一些组合查询页面,需要根据用户输入的查询条件不同生成不同的查询SQL语句,这个操作在JDBC或其他框架中都需要通过SQL的拼接来完成
动态SQL是MyBatis中的重要特性,用来解决这一问题- 判断元素:if(简单的条件判断逻辑,当满足指定条件时追加if元素内的SQL语句) / choose
-
<select> 固定SQL语句 <if test="条件表达式"> 动态SQL语句 </if> <if ...> ... </if> </select>
- choose
<select> 固定SQL语句 <choose> <when test="条件表达式"> 动态SQL语句 </when?> <when ...> ... </when> <otherwise> 动态语句N(当前都不满足时追加的SQL) </otherwise> </choose> </select>
- 关键字元素
- where:主要用于简化查询语句中where部分的条件判断,where元素可以在其出现的位置上输出一个where关键字,而且还可以将后面条件多余的and或or去掉
<select> 固定SQL语句 <where> 根据不同条件动态追加的SQL语句 </where> </select>
- set:用在更新操作时,主要功能和where元素类似,set元素所在的位置输出一个set关键字,而且还可以去掉内容结尾中无关的逗号。
<update> update 表名 <set> 根据条件动态追加要更新的列 //可嵌套if </set> </update>
- trim:在trim包含的内容之前加上某些前缀,也可以在其后面添加后缀
prefix="":前缀
suffix="":后缀
可以把trim包含的内容前面的某些内容过滤,也可以把尾部的某些内容过滤
prefixOverrides="":过滤前面内容
suffixOverrides="":过滤后面内容
<!-- trim元素的使用 模拟where元素的实现 --> <select id="getDeptsByNameAndLoc4" resultType="com.niit.entity.Dept"> select * from dept <trim prefix="where" prefixOverrides="AND|OR"> <if test="dname!=null and dname!=''"> and dname=#{dname} </if> <if test="loc!=null and loc!=''"> and loc=#{loc} </if> </trim> </select>
- where:主要用于简化查询语句中where部分的条件判断,where元素可以在其出现的位置上输出一个where关键字,而且还可以将后面条件多余的and或or去掉
- 循环元素
- foreach
collection="":要迭代的集合
item="":迭代变量
open="":打印集合元素时,以什么元素开始
close="":打印集合元素时,以什么符号结尾
separator="":每个集合元素打印时,其中的分隔符<!-- foreach --> <select id="getDeptByDeptNos" resultType="com.niit.entity.Dept"> select * from dept where deptno in <!-- collection:要迭代的集合 item:迭代变量 open:打印集合元素时,以什么符号开头 close:打印集合元素时,以什么符号结尾 separator:每个集合元素打印时中间的分隔符 --> <foreach collection="deptnos" item="deptno" open="(" close=")" separator=","> #{deptno} </foreach> </select>
- foreach
-
如果语句中有小于号(<)会被解析为XML中的节点,使用CDATA语句块
<![CDATA[ and age<19 ]]>
- 主键映射
-
使用MyBatis做插入操作时,可以由MyBatis负责主键的生成,主键字段的映射分为两种情况
数据库支持自动递增:(MySQL、SqlServer)
数据库不支持自动增长:(Oracle)<insert> <selectKey keyProperty="" resultType="" order=""> </selectKey> </insert>
selectKey指定了入参对象中用来表示主键的属性名,在完成selectKey中语句的执行以后, 会自动将值set到入参对象中由keyProperty指定的属性中去
keyProperty:入参中用来表示主键字段的属性名
order:如果取值为BEFOR,表示先获取主键,再执行插入;对于之处自增长主键的数据库必须设置为AFTER才有效
resultType:生成的主键对应的结果类型
-
- 关联映射
- (1)嵌套查询:通过执行另外一条SQL语句来返回关联数据的结果(至少要查两次)
-
(2)嵌套结果:执行一个表关联查询的SQL语句,然后将查询结果映射成关联的对象(只查一次)
<!-- 关联映射 嵌套查询 --> <select id="getEmpByEmpNo" parameterType="int" resultMap="empMap1"> select * from emp where empno=#{empno} </select> <select id="getDeptByDeptno" parameterType="int" resultType="com.niit.entity.Dept"> select * from dept where deptno=#{deptno} </select> <resultMap id="empMap1" type="com.niit.entity.Emp"> <!-- association元素:用来配置JavaBean类型的关联对象 property:关联对象在当前类中的属性名 column:关联列的列名 javaType:关联对象的java类型 select:指定通过哪一条语句来查询关联对象 --> <association property="dept" column="deptno" javaType="com.niit.entity.Dept" select="getDeptByDeptno"> </association> </resultMap> <!-- 嵌套结果 --> <select id="getEmpByEmpNo2" parameterType="int" resultMap="empMap2"> select e.*,d.* from emp e,dept d where e.deptno=d.deptno and e.empno=#{empno} </select> <resultMap id="empMap2" type="com.niit.entity.Emp"> <result column="empno" property="empno"/> <!-- Emp中其它字段映射关系的配置…… --> <association property="dept" column="deptno" javaType="com.niit.entity.Dept"> <id column="deptno" property="deptno"/> <result column="dname" property="dname"/> <result column="loc" property="loc"/> </association> </resultMap>
- 集合映射
- 嵌套查询:(查询两次)
<!-- 嵌套查询 --> <select id="getDeptInfoByDeptNo" parameterType="int" resultMap="deptMap1"> select * from dept where deptno=#{deptno} </select> <select id="findEmpsByDeptNo" parameterType="int" resultType="com.niit.entity.Emp"> select * from emp where deptno=#{deptno} </select> <resultMap id="deptMap1" type="com.niit.entity.Dept" > <result property="deptno" column="deptno"/> <!-- 返回Dept中关联的Emp列表 --> <!-- collection:关联的集合对象 property:当前类中的集合对象对应的属性 column:当前表中的关联列,通过这个列的值再去查询另外一张表 javaType:返回的集合类型 ofType:返回的集合中元素的泛型 select:指定通过哪条SQL语句来查询关联的集合对象 --> <collection property="emps" column="deptno" javaType="java.util.ArrayList" ofType="com.niit.entity.Emp" select="findEmpsByDeptNo"></collection> </resultMap>
- 嵌套结果:(查询一次)
<!-- 嵌套结果 --> <select id="getDeptInfoByDeptNo2" parameterType="int" resultMap="deptMap2"> select e.*,d.* from emp e,dept d where e.deptno=d.deptno and d.deptno=#{deptno} </select> <resultMap id="deptMap2" type="com.niit.entity.Dept"> <id column="deptno" property="deptno"/> <result column="dname" property="dname"/> <result column="loc" property="loc"/> <collection property="emps" column="deptno" ofType="com.niit.entity.Emp" javaType="java.util.ArrayList"> <id column="empno" property="empno"/> <result column="ename" property="ename"/> <!-- Emp中的其它字段和属性的映射关系 --> </collection> </resultMap>
使用<collection>进行dept对象中的List<Emp> emps;的注入。只执行了一次SQL语句,返回一个 Dept 对象。该标签中的列名当前表中的关联列,通过这个列的值再去查询另外一张表
<id />和<result />基本一致
Dept.javaprivate int deptno; private String dname; private String loc; private List<Emp> emps;
Emp.javaprivate int empno; private String ename; private String job; private int mgr; private Date hiredate; private double sal; private double comm; private int deptno; /** dept就是员工对应的部门对象 **/ private Dept dept;
- 嵌套查询:(查询两次)