MyBatis ¶
概述 ¶
三层架构 ¶
- 界面层(springmvc):和用户打交道,接受用户请求参数,显示处理结果(jsp,html,servlet)
- 业务逻辑层(spring):接受界面层传递数据,计算逻辑,调用数据库,获取数据
- 数据访问层(持久层,MyBatis):访问数据库,对数据进行查询、删除、修改
用户使用界面层—>业务逻辑层—>数据访问层(持久层)—>数据库
框架(Framework) ¶
- 框架是一个模板、软件;定义好了一些可重复使用的基础功能
- 规定好了一些条款和内容;可以加入自己的东西
- 特点
- 框架一般不是全能的
- 框架是针对某一个领域有效
- 框架是一个软件
JDBC缺陷 ¶
- 代码量很多,大量的代码重复,效率较低,业务代码和数据库操作混在一起
MyBatis简述 ¶
- MyBatis是apache的一个开源项目,是一个开源的持久层框架,用户数据库操作
- MyBatis是一个sql映射框架,数据访问(DAOs);可以将数据库一行数据映射为一个java对象
- 功能:
- 提供了创建Connection、Statement、ResultSet的能力,不需要开发人员创建这些对象
- 提供了执行sql语句的能力
- 提供了循环sql,吧sql的结果转化为java对象,List集合的能力
- 提供了关闭资源的能力,不需要你关闭Connection、Statement、ResultSet
MyBatis框架快速入门 ¶
下载 ¶
- 在github上面可以直接下载,然后导入项目即可
实现步骤 ¶
新建student表
导入MyBatis包和mysql包
新建实体类Student,对应表中一行数据
创建持久层dao接口,定义操作数据库的方法
创建一个MyBatis使用的配置文件 叫做sql映射文件,一般一个表对应一个映射文件 这个文件是xml文件
xml1<?xml version="1.0" encoding="UTF-8" ?> 2<!DOCTYPE mapper 3 PUBLIC "-//MyBatis.org//DTD Mapper 3.0//EN" 4 "http://MyBatis.org/dtd/MyBatis-3-mapper.dtd"> 5<mapper namespace="com.lei.dao.StudentDao"> 6 <!-- 7 id:是你要执行sql语句的唯一标识,MyBatis会根据你这个id值来找到你需要执行的sql语句, 8 你可以自定义,但要求使用接口中的方法名 9 resultType:表示sql语句执行后,返回什么类型,值写的是类型的全限定名称 10 --> 11 <select id="selectStuds" resultType="com.lei.domain.Student"> 12 select * from t_student order by id 13 </select> 14</mapper> 15<!-- 161.指定约束文件为MyBatis-3-mapper.dtd 17 <!DOCTYPE mapper 18 PUBLIC "-//MyBatis.org//DTD Mapper 3.0//EN" 19 "http://MyBatis.org/dtd/MyBatis-3-mapper.dtd"> 202.mapper当前文件根标签 21 <mapper namespace="test"></mapper> 22 namespace叫做命名空间,唯一值的,要求使用dao接口的全限定名称 233.在当前文件使用特定标签,表述数据库的特定操作 24 <select id="selectBlog" resultType="Blog"> 25 select * from Blog where id = #{id} 26 </select> 27 28 <select>:表示查询,select语句 29 <update>:表示数据库更新操作 30 <inster>:表示插入,放的是insert语句 31 <delete>:表示删除,放的是delete语句 32-->创建MyBatis主配置文件: 一个项目就一个主配置文件 主配置文件提供了数据库连接信息和sql文件映射的位置信息
xml1<?xml version="1.0" encoding="UTF-8" ?> 2<!DOCTYPE configuration 3 PUBLIC "-//MyBatis.org//DTD Config 3.0//EN" 4 "http://MyBatis.org/dtd/MyBatis-3-config.dtd"> 5<configuration> 6 <!--MyBatis设置,控制其全局行为--> 7 <settings> 8 <!--设置MyBatis输出日志--> 9 <setting name="logImpl" value="STDOUT_LOGGING"/> 10 </settings> 11 <!-- 和spring整合后 environments配置将废除--> 12 <environments default="development"> 13 <environment id="development"> 14 <!-- type="JDBC" 代表使用JDBC的提交和回滚来管理事务 --> 15 <transactionManager type="JDBC" /> 16 <!-- MyBatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI --> 17 <!-- POOLED 表示支持JDBC数据源连接池 --> 18 <!-- UNPOOLED 表示不支持数据源连接池 --> 19 <!-- JNDI 表示支持外部数据源连接池 --> 20 <dataSource type="POOLED"> 21 <property name="driver" value="com.mysql.jdbc.Driver"/> 22 <property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/> 23 <property name="username" value="root"/> 24 <property name="password" value="123456"/> 25 </dataSource> 26 27 </environment> 28 </environments> 29 <mappers> 30 <mapper resource="com\lei\dao\StudentDao.xml"/> 31 </mappers> 32</configuration> 33<!-- 34 MyBatis的主配置文件,定义了数据库配置以及sql映射文件配置 35 <configuration> 36 <environment> 37 环境配置 38 </environment> 39 <mappers> 40 一个mapper标签指定一个文件的位置 41 从类路径开始的路径信息 42 </mappers> 43 </configuration> 44-->创建使用的MyBatis类 通过MyBatis访问数据库
java1public static void main(String[] args) throws IOException { 2 String resource = "MyBatis-config.xml"; 3 // 通过流将核心配置文件读取进来 4 InputStream inputStream = null; 5 inputStream = Resources.getResourceAsStream(resource); 6 // 通过核心配置文件创建会话工厂 7 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 8 SqlSessionFactory factory=builder.build(inputStream); 9 // 通过会话工厂创建会话 10 SqlSession session = factory.openSession(); 11 //找到对应的sql语句,并执行 12 String sqlId="com.lei.dao.StudentDao"+"."+"selectStuds"; 13 List<Student> studs=session.selectList(sqlId); 14 for(Student s:studs){ 15 System.out.println(s); 16 } 17 session.close(); 18}
插入操作 ¶
StudentDao.xml
1<insert id="insertStud">
2 insert into t_student values(#{id},#{name},#{age})
3</insert>调用
1try {
2 SqlSessionFactory factory=Mybatis.getSqlSessionFactory();
3 session=factory.openSession();
4 res=session.insert("com.lei.dao.StudentDao.insertStud",stud);
5} catch (IOException e) {
6 e.printStackTrace();
7}finally {
8 if (session!=null){
9 session.commit();//MyBatis默认不自动提交事务
10 session.close();
11 }
12}使用 SqlSession ¶
在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。 一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。 使用 MyBatis-Spring 之后,你不再需要直接使用 SqlSessionFactory 了,因为你的 bean 可以被注入一个线程安全的 SqlSession,它能基于 Spring 的事务配置来自动提交、回滚、关闭 session
SqlSessionTemplate ¶
当调用 SQL 方法时(包括由 getMapper() 方法返回的映射器中的方法),SqlSessionTemplate 将会保证使用的 SqlSession 与当前 Spring 的事务相关
MyBatis框架Dao代理 ¶
主要类的介绍 ¶
Resources:MyBatis中的一个类,负责读取配置文件
InputStream in=Resources.getResourceAsStream(“MyBatis.xml”)
SqlSessionFactoryBuilder:负责创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder()
SqlSessionFactory factory=builder.build(in)
SqlSessionFactory 重量级对象,创建时间长,消耗资源多;整个程序有一个就够了
SqlSessionFactory:接口;DefaultSqlSessionFactory:SqlSessionFactory 接口实现类
SqlSessionFactory 作用:获取SqlSession对象;SqlSession sqlsession=factory.openSession();openSession()无参,获取非自动提交事务的SqlSession对象;openSession(true),获取事务自动提交的SqlSession对象
SqlSession接口:定义了操作数据的方法;例如:selectOne()、selectList()、insert()、commit()、rollback()等
SqlSession接口实现类:DefaultSqlSession
使用要求:SqlSession对象不是线程安全的,需要在方法内部使用,在执行sql语句之前,先获取SqlSession对象,执行完后关闭SqlSession它,这样可以保证线程安全
MyBatis工具类的创建 ¶
1public class MybatisUtil {
2 private static SqlSessionFactory factory=null;
3 static {
4 String resource = "MyBatis-config.xml";
5 // 通过流将核心配置文件读取进来
6 InputStream inputStream = null;
7 try {
8 inputStream = Resources.getResourceAsStream(resource);
9 } catch (IOException e) {
10 e.printStackTrace();
11 }
12 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
13 factory=builder.build(inputStream);
14 }
15
16 /**
17 * 非自动提交事务的sqlsession对象
18 * @return sqlsession对象
19 */
20 public static SqlSession getSqlSession() {
21 SqlSession sqlSession=null;
22 if (factory!=null){
23 // 通过核心配置文件创建会话工厂
24 sqlSession=factory.openSession();
25 }
26 return sqlSession;
27 }
28 /**
29 * 自动提交事务的sqlseesion对象
30 * @return sqlsession对象
31 */
32 public static SqlSession getSqlSession(boolean flag) {
33 SqlSession sqlSession=null;
34 if (factory!=null){
35 // 通过核心配置文件创建会话工厂
36 sqlSession=factory.openSession(flag);
37 }
38 return sqlSession;
39 }
40}MyBatis动态代理 ¶
因为mapper的命名空间与dao接口的全限定名称对应、同时相关sql语句id与dao接口中的方法对应,
以上两中对应是一种规定,命名时注意;根据返回值类型,调用对应的方法,如果是List,调用sqlSession.selectList()方法;如果返回值是int,则查看mapper文件中的标签,从而执行对应的方法,
MyBatis根据dao方法调用,获取执行sql语句信息;MyBatis根据你得dao接口,创建出一个dao接口实现类,并创建这个类的对象;完成SqlSession调用方法去访问数据库
不要使用重载方法,因为sql语句id根据方法名命名
1/**
2* 通过MyBatis的动态代理,sqlSession.getMapper(StudentDao.class)
3* MyBatis能通过上述代码,取得StudentDao接口的实例对象(像创建实现类,再创建接口)
4* 执行查询操作,注意mapper中的namespace与sql语句id 对应 Dao接口的全限定名称和方法名
5*/
6public static void selectStud(){
7 SqlSession sqlSession= Mybatis.getSqlSession();
8 StudentDao dao=sqlSession.getMapper(StudentDao.class);
9 //查询操作
10 List<Student> studs=dao.selectStuds();
11 for (Student stud:studs){
12 System.out.println(stud);
13 }
14}
15public static void insertStud(){
16 SqlSession sqlSession= Mybatis.getSqlSession();
17 StudentDao dao=sqlSession.getMapper(StudentDao.class);
18 // 插入操作
19 int res=dao.insertStud(new Student("A0020","刘备",32));
20 System.out.println(res);
21}Mybatis传参 ¶
parameterType:写在mapper文件中的一个属性,表示dao接口中方法的参数的数据类型; parameterType 是dao接口中方法参数的数据类型,它的值是java数据类型的全限定名称或者MyBatis定义的别名(MyBatis文档可以看到,都是数据类型的小写); 这个值可以没有,不是强制的,MyBatis通过反射机制能够发现接口参数的数据类型
简单类型参数:MyBatis把java的基本数据类型和string都看作简单类型 在mapper中获取一个简单类型参数的值,使用**#{任意字符}** MyBatis:创建JDBC连接对象–>PrepareStatement对象(预编译)–>设置sql语句中占位符值,并执行sql–>将查询结果封装返回
多个参数,使用**@Param**命名参数(推荐使用)
java1//通过注解,定义方法时写上对应参数的名字 2List<Student> selectStuds(@Param("id") String id,@Param("age") Integer age);mapper文件中
xml1<!--写上方法中对应参数名--> 2<select id="selectStuds" resultType="com.lei.domain.Student"> 3 select * from t_student where id=#{id} or age=#{age} 4</select>多个参数,使用对象方式**#{属性名,javaType=类型名称,jdbcType=数据类型}**
javaType:指java类型中属性的数据类型
jdbcType:指在数据库中的数据类型(MyBatis文档可查)
例如:#{paramName,javaType=java.lang.String,jdbcType=VARCHAR}
以上时完整的,平常开发使用简化版,#{属性名},可以通过反射获取javaType,jdbcType的值,不需要指定类型
java1/*创建java类,通过传递参数到mapper*/ 2public class Student { 3 private String id; 4 private String name; 5 private Integer age; 6 //。。。。 7}mapper文件中
xml1<!--写类中对应属性名,作为参数--> 2<select id="selectStuds" resultType="com.lei.domain.Student"> 3 select * from t_student where id=#{id} or age=#{age} 4</select>多个参数,按位置顺序传参(了解);MyBatis3.4版本后#{arg0},#{arg1}等,获取参数
map传参,通过key获取value值(了解);#{key1},#{key2}
#和$的区别 ¶
#:可以防止sql注入,占位符,使用预编译对象PrepareStatement执行sql,安全 $:字符串的连接与替换,直接拼接到sql语句中,使用对象Statement执行sql,可以被sql注入,效率低
封装MyBatis输出结果 ¶
MyBatis执行sql语句,得到java对象
resultType结果类型,指sql语句执行结束后,数据转化为java对象,这个java类型是任意的
- MyBatis执行sql语句,然后MyBatis调用类的无参构造,创建对象
- MyBatis把resultSet指定列的值赋值给对象同名属性
- resultType值有两种:MyBatis规定的别名、全限定名称
定义resultType 自定义类型的别名
- 在MyBatis的主配置文件中,<configuration>标签下定义,使用时直接给resultType值就行
xml1<!--方式1:type是全限定名称,alias是定义的别名--> 2<configuration> 3 <typeAliases> 4 <typeAlias type="java类型的全限定名称" alias="stu" /> 5 </typeAliases> 6</configuration> 7<!--方式2;name是包名,然后该包下所有的类名就是别名--> 8<configuration> 9 <typeAliases> 10 <package name="com.lei.domain"/> 11 </typeAliases> 12</configuration> 13 14<!--在mapper中使用--> 15<select id="selectId" resultType="stu"> 16 select * from t_student where id=#{id} 17</select>resultType 返回map
- 列值是map的value
- 且只能查询一条记录返回,多行记录返回会报错
resultMap:结果映射,指定列名和java对象属性对应关系
- 自定义列值赋值给对象的哪个属性
- reslutMap与resultType不能一起使用
- 当你对象属性名与列名不一样时,一定使用resultMap
先定义resultMap;然后再mapper标签中引用定义resultMap的id就可
xml1<!--mapper文件--> 2<!--定义resultMap--> 3<resultMap id="map1" type="java类型的全限定名称"> 4 <!--主键列--> 5 <id colum="列名" property="属性名"/> 6 <!--非主键列--> 7 <result colum="列名" property="属性名"/> 8</resultMap> 9 10<!--引用resultMap,此时MyBatis根据mapper对应映射关系--> 11<select id="selectId" restypeMap="map1"> 12 select * from t_student 13</select>列名与属性名不一致的第二种解决办法(第一种是定义resultMap)
- resultType默认原则,同名的列值赋给同名的属性
- 可以使用别名方式查询,别名应该是java类型的属性名
like模糊查询 ¶
方式一:在java代码中指定like的内容,推荐
java1//java代码中准备好like模糊查询的的内容 2//name应该为拼接好的like值,例如:%李% 3List<Student> selectLike(String name); 4 5//mapper中内容应该为 6<select id="selectAll" resultMap="studentMap"> 7 select * from t_student like #{name} 8</select>方式二:在mapper文件中来拼接like内容
xml1<!--此时name传入的值应该为: 李 然后再mapper中拼接--> 2<select id="selectAll" resultMap="studentMap"> 3 select * from t_student like "%" #{name} "%" 4</select>
多表联查 ¶
domain不能代表返回值,解决方案:map 或者 vo
- 可以使用map返回值类型,sql语句中可以取别名
- 可以使用vo,作为多表联查返回值类型;需要创建出一个类,类中属性由我们自己定义,属性会包含所需要展示的信息
MyBatis框架动态SQL ¶
简介 ¶
- 动态sql,指的是sql语句是变化的,可以根据条件获取不同的sql语句
- 动态sql语句的实现,使用的是MyBatis提供的标签 <if>,<where>,<foreach>
- 动态sql必须使用java对象作为参数
<if>标签 ¶
<if>是判断条件的,语法如下
xml1<!--test中语法:属性=xxx值 --> 2<if test="判断java对象的属性值"> 3 部分sql语句 4</if> 5 6<!--mapper文件中实例 7 where 后代码:where 1=1 防止sql语法错误 8--> 9<select id="selectAll" resultType="com.lei.domain.Student"> 10 select * from t_student where 1=1 11 <if test="name !=null and name!=' ' "> 12 name=#{name} 13 </if> 14 <if test=" age>0 "> 15 and age>#{age} 16 </if> 17</select>
<where>标签 ¶
<where>用来包含多个<if>的,当多个if有一个成立,<where>标签会自动增加一个where关键字,并去掉最后sql中多余的or,and 防止语法错误
xml1<!--mapper文件中实例 2 where 后代码:where 1=1 防止sql语法错误 3--> 4<select id="selectAll" resultType="com.lei.domain.Student"> 5 select * from t_student 6 <where> 7 <if test="name !=null and name!=' ' "> 8 name=#{name} 9 </if> 10 <if test=" age>0 "> 11 and age>#{age} 12 </if> 13 </where> 14</select>
<foreach>标签 ¶
循环标签,用户循环java中数组、List集合的。主要用于sql的in语句中
参数意义
- collection:表示接口中的方法参数类型,如果是数组使用array,如果是list集合使用list
- item:自定义的,表示数组和集合成员的变量
- open:循环开始时的字符
- close:循环结束时的字符
- separator:集合成员之间的分隔符
xml1<!-- 2 学生id是:1001,1002,1003的三个学生 3 select * from student where id in (1001,1002,1003) 4 5 public List<Student> selectFor(List<Integer> idList); 6 7 List<Integer> idList=new ...; 8 idList.add(1001); 9 idList.add(1002); 10 idList.add(1003); 11 dao.selectFor(idList); 12 13 <foreach>可以将list集合中内容循环遍历,并拼接为sql语句 14--> 15 16<!--foreach使用1,List<Integer> --> 17<select id="selectAll" resultType="com.lei.domain.Student"> 18 select * from t_student where id in 19 <foreach collection="list" item="myid" open="(" separator="," close=")" > #{myid} </foreach> 20</select> 21 22<!--foreach使用2,List<Object>,拿到属性名--> 23<select id="selectAll" resultType="com.lei.domain.Student"> 24 select * from t_student where id in 25 <foreach collection="list" item="obj" open="(" separator="," close=")" > #{obj.id} </foreach> 26</select>
动态sql的代码片段 ¶
- 复用sql语句
- 首先使用<sql id=“唯一id”>标签定义一个代码片段
- 然后使用<include refid=“自定义片段的id " >标签引用代码片段
配置文件介绍 ¶
属性配置文件 ¶
在根目录下定义一个属性配置文件,xxx.properties,例如:jdbc.properties
properties1jdbc.driver=com.mysql.jdbc.Driver 2jdbc.url=jdbc:mysql://192.168.220.130:3306/mytest?characterEncoding=utf-8 3jdbc.user=root 4jdbc.password=123456在mybaties主配置文件中,使用<properties>标签指定文件位置;然后在需要使用值的地方:${key}
引入mapper文件 ¶
方式一:mapper
xml1 <mappers> 2 <mapper resource="com\lei\dao\StudentDao.xml"/> 3 <mapper resource="com\lei\dao\OderDao.xml"/> 4</mappers>方式二:package(mapper与dao文件同一目录且文件名相同)
xml1 <mappers> 2 <!-- 3 这个包中所有xml文件可以一次加载给MyBatis 4 要求:dao接口与mapper文件名一样且在同一目录 5 --> 6 <package resource="包名"/> 7</mappers>
主配置文件 ¶
1<?xml version="1.0" encoding="UTF-8" ?>
2<!DOCTYPE configuration
3 PUBLIC "-//MyBatis.org//DTD Config 3.0//EN"
4 "http://MyBatis.org/dtd/MyBatis-3-config.dtd">
5<configuration>
6 <!-- 属性配置文件位置-->
7 <properties resource="jdbc.properties"></properties>
8
9 <!--
10 MyBatis设置,控制其全局行为,与数据库交互环境(二级缓存,加载策略...)
11 对于海量级别数据,如何提高查询效率
12 基础操作:
13 对于常用查询字段,设置索引(加目录)
14 高级操作
15 使用nosql数据库,redis
16 专业操作
17 针对于电商行业,搜索引擎(Elasticsearch和Solr)
18 -->
19 <settings>
20 <!-- 设置MyBatis输出日志-->
21 <setting name="logImpl" value="STDOUT_LOGGING"/>
22 </settings>
23
24 <!-- 为mapper映射文件的domain起别名 -->
25 <typeAliases>
26 <!--
27 方式1:为指定的类分别起别名,别名的命名有我们自己决定
28 <typeAlias type="com.lei.domain.Student" alias="Student"/>
29 type:指定为那个domain起别名
30 alias:别名名字
31 方式2:使用package标签,批量起别名,别名是MyBatis默认取好的,命名不由我们自己决定
32 <package name="com,lei,domain" />
33 name:表示在该包下,所有domain自动起好了别名,别名就是类名
34 -->
35 </typeAliases>
36
37 <!-- 插件配置-->
38 <plugins>
39 <plugin interceptor="com.github.pagehelper.PageInterceptor" />
40 </plugins>
41
42 <!-- 和spring整合后 environments配置将废除-->
43 <environments default="development">
44 <environment id="development">
45 <!-- type="JDBC" 代表使用JDBC的提交和回滚来管理事务 -->
46 <transactionManager type="JDBC" />
47 <!--
48 dataSource:表示数据源,在java体系中,规定实现javax.sql.DataSource接口的都是数据源
49 MyBatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI
50 POOLED 表示支持JDBC数据源连接池
51 UNPOOLED 表示不支持数据源连接池;每次执行sql,都会先创建链接,执行sql,关闭连接
52 JNDI 表示支持外部数据源连接池,java命名和目录(类似windows注册表)
53 -->
54 <dataSource type="POOLED">
55 <property name="driver" value="${jdbc.driver}" />
56 <property name="url" value="${jdbc.url}"/>
57 <property name="username" value="${jdbc.user}"/>
58 <property name="password" value="${jdbc.password}"/>
59 </dataSource>
60
61 </environment>
62 </environments>
63 <!--指定mapper映射文件-->
64 <mappers>
65 <mapper resource="com\lei\dao\StudentDao.xml"/>
66 <!--
67 方式1:使用resource属性,指定mapper映射文件
68 <mapper resource="com\lei\dao\StudentDao.xml"/>
69 方式2:使用class属性,找到dao层接口的全路径
70 <mapper class="com.lei.dao.StudentDao"/>
71 方式3:使用package 批量注册
72 <package name="" />
73 name:指向dao层包,表示该包下所有mapper映射文件加载
74-->
75 </mappers>
76</configuration>
77 <!--
78 MyBatis的主配置文件,定义了数据库配置以及sql映射文件配置
79 <configuration>
80 <environment>
81 环境配置
82 </environment>
83 <mappers>
84 一个mapper标签指定一个文件的位置
85 从类路径开始的路径信息
86 </mappers>
87 </configuration>
88 -->mapper映射文件 ¶
1<?xml version="1.0" encoding="UTF-8" ?>
2<!DOCTYPE mapper
3 PUBLIC "-//MyBatis.org//DTD Mapper 3.0//EN"
4 "http://MyBatis.org/dtd/MyBatis-3-mapper.dtd">
5<mapper namespace="com.lei.dao.OrderDao">
6 <!--
7 paramterType
8 使用简单类型的参数,在 #{} 中的名字可以随便写
9 传递多个参数
10 1.使用在方法处使用:@Param命名参数
11 List<Student> selectStuds(@Param("id") String id,@Param("age") Integer age);
12 2.传递对象
13 #{} 中必须为对象的属性
14 3.传递map
15 #{} 中必须为map的key
16 parameterType 一般不写,MyBatis会自动识别
17 -->
18 <!--
19 #{}:占位符,可以防止sql注入
20 ${}:字符串拼接
21 -->
22 <!--
23 模糊查询
24 方式1:使用 ${} ,拼接sql(了解,不常用)
25 select * from t_student where name like '%${value}%'
26 方式2:使用 #{} ,占位符,直接传入拼接好的条件字符串
27 String value="%z%";
28 select * from t_student where name like #{value}
29 方式3:使用 #{} ,占位符,传入条件字符串,在sql中拼接,sql中空格相当于拼接
30 select * from t_student where name like % #{value} %
31 -->
32 <!--
33 resultType与resultMap
34 resultType 可以为domain类型(常用),也可以是map;
35 在domain类型封装不了结果类型时,使用map
36 例如:根据姓名分组,并返回每一个姓名对应的数量,使用map进行封装;查询字段与doamin属性不一致时 都使用map
37 resultMap domain中类型属性名与数据库表字段名一一对应
38 <resultMap id="唯一名称" type="domain类型">
39 <id property="domain中属性名" column="主键字段" />
40 <result property="domain中属性名" column="非主键字段" />
41 </resultMap>
42 <select id="" resultMap="resultMap的唯一id" />
43 -->
44 <!--
45 动态sql:有什么查询条件,在sql语句where后添加什么条件
46 <where>标签里面必有<if>标签,如果有条件,则展现where关键字,如果没有条件则不展现where关键字
47 并且<where>标签会自动屏蔽掉第一个连接符 and/or
48 <if test=" 属性名1!=null and 属性名1!='' " > </if>
49 <foreach>标签用来遍历传过来的数组,常用在sql语句的in关键字后
50
51 <where>
52 <if test="条件"> name like '%'#{name}'%' </if>
53 </where>
54 <foreach collection="array/list" item="" open="循环开始符号" close="循环结束符号" separator="元素之间分隔符" />
55 -->
56 <!--
57 sql片段:常用在重复率高且复杂的子查询,大量的sql片段会降低代码的可读性
58 定义:<sql id="sql片段的唯一标识">重复的sql代码</sql>
59 引用:<include refid="sql片段id" />
60 -->
61
62 <select id="select1" parameterType="java.lang.String" resultType="Order">
63 select * from tb_student where id=#{id}
64 </select>
65</mapper>插件 ¶
MyBatis允许用户在已映射语句执行过程中的某一点进行拦截调用。MyBatis使用插件来拦截的方法调用,故此MyBatis插件通常称为:Mybatis拦截器。默认情况下,MyBatis允许使用插件来拦截的对象包括下面的四个:
1Executor:拦截执行器的方法。
2ParameterHandler:拦截参数的处理。
3ResultHandler:拦截结果集的处理。
4StatementHandler:拦截Sql语法构建的处理。@Intercepts:标识该类是一个拦截器
@Signature:指明自定义拦截器需要拦截哪一个类型,哪一个方法
- type:上述四种类型中的一种;
- method:对应接口中的哪类方法(因为可能存在重载方法)
- args:对应哪一个方法的入参
相关注解介绍 ¶
类上使用注解
@Intercepts,定义拦截信息,只有符合条件的才会进入拦截,参数为拦截点java1@Documented 2@Retention(RetentionPolicy.RUNTIME) 3@Target(ElementType.TYPE) 4public @interface Intercepts { 5 /** 6 * 定义拦截点 7 * 只有符合拦截点的条件才会进入到拦截器 8 */ 9 Signature[] value(); 10}使用
@Signature注解定义拦截点,指定需要拦截哪个类对象的哪个方法java1@Documented 2@Retention(RetentionPolicy.RUNTIME) 3@Target({}) 4public @interface Signature { 5 /** 6 * 定义拦截的类 Executor、ParameterHandler、StatementHandler、ResultSetHandler当中的一个 7 */ 8 Class<?> type(); 9 10 /** 11 * 在定义拦截类的基础之上,在定义拦截的方法 12 */ 13 String method(); 14 15 /** 16 * 在定义拦截方法的基础之上在定义拦截的方法对应的参数, 17 * JAVA里面方法可能重载,故注意参数的类型和顺序 18 */ 19 Class<?>[] args(); 20}
@Intercepts注解的使用规则
1@Intercepts({//这里可以定义多个@Signature对多个地方拦截,都用这个拦截器
2 @Signature(
3 type = ResultSetHandler.class,
4 method = "handleResultSets",
5 args = {Statement.class}),
6 @Signature(type = Executor.class,
7 method = "query",
8 args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
9})拦截器实例 ¶
编写拦截器实现sql语句打印,以及执行时间打印
编写拦截器,实现
Interceptor接口java1@Intercepts(value = { 2 @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), 3 @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), 4 @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) 5}) 6@Slf4j 7public class ExamplePlugin2 implements Interceptor { 8 9 //方法拦截 10 @Override 11 public Object intercept(Invocation invocation) throws Throwable { 12 StopWatch stopWatch=new StopWatch(); 13 try { 14 stopWatch.start("sql执行任务"); 15 return invocation.proceed();//执行sql过程 16 }catch (Exception e){ 17 e.printStackTrace(); 18 throw e; 19 }finally { 20 MappedStatement statement= (MappedStatement) invocation.getArgs()[0]; 21 Object parameter = ""; 22 if (invocation.getArgs().length>1){ 23 parameter = invocation.getArgs()[1]; 24 } 25 BoundSql boundSql = statement.getBoundSql(parameter);// BoundSql就是封装myBatis最终产生的sql类 26 Configuration configuration = statement.getConfiguration();// 获取节点的配置 27 String sql = getSql(configuration, boundSql); 28 log.info(sql); 29 stopWatch.stop(); 30 log.info(stopWatch.getLastTaskName()+":"+stopWatch.getLastTaskTimeMillis()+"ms"); 31 } 32 } 33 34 /** 35 * 获取带参SQL 36 */ 37 public static String getSql(Configuration configuration, BoundSql boundSql) { 38 Object parameterObject = boundSql.getParameterObject();//获取参数 39 List<ParameterMapping> parameterMappings = boundSql 40 .getParameterMappings(); 41 String sql = boundSql.getSql().replaceAll("[\\s]+", " ");//sql语句中多个空格都用一个空格代替 42 if (parameterMappings.size() > 0 && parameterObject != null) { 43 TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); //获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换。如果根据parameterObject.getClass()可以找到对应的类型,则替换 44 if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { 45 sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject))); 46 } else { 47 MetaObject metaObject = configuration.newMetaObject(parameterObject);//MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作 48 for (ParameterMapping parameterMapping : parameterMappings) { 49 String propertyName = parameterMapping.getProperty(); 50 sql = formatSql(boundSql, sql, metaObject, propertyName); 51 } 52 } 53 } 54 return sql; 55 } 56 57 /** 58 * 格式化SQL(替换?) 59 */ 60 private static String formatSql(BoundSql boundSql, String sql, MetaObject metaObject, String propertyName) { 61 if (metaObject.hasGetter(propertyName)) { 62 Object obj = metaObject.getValue(propertyName); 63 sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj))); 64 } else if (boundSql.hasAdditionalParameter(propertyName)) { 65 Object obj = boundSql.getAdditionalParameter(propertyName);//该分支是动态sql 66 sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj))); 67 } else { 68 sql = sql.replaceFirst("\\?", "缺失");//打印出缺失,提醒该参数缺失并防止错位 69 } 70 return sql; 71 } 72 73 /** 74 * 对SQL进行格式化 75 */ 76 private static String getParameterValue(Object obj) { 77 String value = null; 78 if (obj instanceof String) { 79 value = "'" + obj.toString() + "'"; 80 } else if (obj instanceof Date) { 81 DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA); 82 value = "'" + formatter.format(obj) + "'"; 83 } else { 84 if (obj != null) { 85 value = obj.toString(); 86 } else { 87 value = ""; 88 } 89 } 90 return value; 91 } 92 93 //获取到拦截的对象,底层也是通过代理实现的,实际上是拿到一个目标代理对象 94 @Override 95 public Object plugin(Object target) { 96 return Plugin.wrap(target, this); 97 } 98 99}注册该插件(拦截器)到MyBatis的拦截器链中
java1@Bean 2public MybatisInterceptor MyBatisInterceptor(SqlSessionFactory sqlSessionFactory) { 3 MybatisInterceptor plugin2=new MybatisInterceptor(); 4 sqlSessionFactory.getConfiguration().addInterceptor(plugin2); 5 return plugin2; 6}或
xml1 2<!-- 插件配置,在<environments> 标签上面--> 3<plugins> 4 <plugin interceptor="com.lei.config.ExamplePlugin2" /> 5</plugins>
扩展 ¶
PageHelper ¶
PageHelper用来做数据分页的
在MyBatis主配置文件中配置插件
xml1 <!-- 插件配置,在<environments> 标签上面--> 2 <plugins> 3 <plugin interceptor="com.github.pagehelper.PageInterceptor" /> 4 </plugins>java中使用
java1 public static void selectAll(){ 2 SqlSession sqlSession= Mybatis.getSqlSession(); 3 StudentDao dao=sqlSession.getMapper(StudentDao.class); 4 //加入pagehelper的方法,分页;参数为 : 第几页开始,每页多少数据 5 PageHelper.startPage(1,3); 6 List<Student> stus=dao.selectAll(); 7 for (Student a:stus){ 8 System.out.println(a); 9 } 10 }
工具类 ¶
MyBatis封装
java1public class SqlSessionUtil { 2 private static SqlSessionFactory sessionFactory; 3 //静态代码块,只执行一次,类被加载时 4 static { 5 try { 6 InputStream inputStream= Resources.getResourceAsStream("MyBatis-config.xml"); 7 sessionFactory=new SqlSessionFactoryBuilder().build(inputStream); 8 } catch (IOException e) { 9 e.printStackTrace(); 10 } 11 } 12 13 //私有化构造方法 14 private SqlSessionUtil(){} 15 16 //线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的;保证同一个SqlSession对象 17 private static ThreadLocal<SqlSession> t=new ThreadLocal<>(); 18 19 //取得SqlSession对象 20 public static SqlSession getSqlSession(){ 21 SqlSession sqlSession=t.get(); 22 if (sqlSession==null){ 23 sqlSession=sessionFactory.openSession(); 24 t.set(sqlSession); 25 } 26 return sqlSession; 27 } 28 29 //关闭SqlSession对象 30 public static void closeSqlSession(SqlSession sqlSession){ 31 if (sqlSession!=null){ 32 sqlSession.close(); 33 //用户发送请求,服务器分配一条线程,执行结束后,线程回到线程池中,并没有被销毁;所以需要移除,保证线程的干净 34 t.remove(); 35 } 36 } 37}动态代理封装
java1public class TransactionInvocationHandler implements InvocationHandler { 2 private Object target; 3 4 public TransactionInvocationHandler(Object target) { 5 this.target = target; 6 } 7 8 @Override 9 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 10 SqlSession sqlSession=null; 11 Object obj=null; 12 try{ 13 sqlSession=SqlSessionUtil.getSqlSession(); 14 //处理业务逻辑 15 obj=method.invoke(target,args); 16 //业务逻辑完毕后,提交事务 17 sqlSession.commit(); 18 }catch (Exception e){ 19 //出错回滚 20 sqlSession.rollback(); 21 e.printStackTrace(); 22 }finally { 23 SqlSessionUtil.closeSqlSession(sqlSession); 24 } 25 return obj; 26 } 27 28 /** 29 * 取得代理对象 30 * @return 代理对象 31 */ 32 public Object getProxy(){ 33 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 34 target.getClass().getInterfaces(), 35 this); 36 } 37}
manev配置 ¶
1<?xml version="1.0" encoding="UTF-8"?>
2
3<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5 <modelVersion>4.0.0</modelVersion>
6
7 <groupId>org.example</groupId>
8 <artifactId>ch03</artifactId>
9 <version>1.0-SNAPSHOT</version>
10 <packaging>war</packaging>
11
12 <name>ch03 Maven Webapp</name>
13 <!-- FIXME change it to the project's website -->
14 <url>http://www.example.com</url>
15
16 <properties>
17 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18 <maven.compiler.source>1.7</maven.compiler.source>
19 <maven.compiler.target>1.7</maven.compiler.target>
20 </properties>
21
22 <dependencies>
23 <dependency>
24 <groupId>junit</groupId>
25 <artifactId>junit</artifactId>
26 <version>4.11</version>
27 <scope>test</scope>
28 </dependency>
29 <dependency>
30 <groupId>javax.servlet</groupId>
31 <artifactId>servlet-api</artifactId>
32 <version>2.5</version>
33 </dependency>
34 <dependency>
35 <groupId>javax.servlet.jsp</groupId>
36 <artifactId>jsp-api</artifactId>
37 <version>2.2</version>
38 </dependency>
39 <dependency>
40 <groupId>org.MyBatis</groupId>
41 <artifactId>MyBatis</artifactId>
42 <version>3.5.5</version>
43 </dependency>
44 <dependency>
45 <groupId>mysql</groupId>
46 <artifactId>mysql-connector-Java</artifactId>
47 <version>5.1.3</version>
48 </dependency>
49 </dependencies>
50
51 <build>
52 <finalName>ch03</finalName>
53 <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
54 <plugins>
55 <plugin>
56 <artifactId>maven-clean-plugin</artifactId>
57 <version>3.1.0</version>
58 </plugin>
59 <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
60 <plugin>
61 <artifactId>maven-resources-plugin</artifactId>
62 <version>3.0.2</version>
63 </plugin>
64 <plugin>
65 <artifactId>maven-compiler-plugin</artifactId>
66 <version>3.8.0</version>
67 </plugin>
68 <plugin>
69 <artifactId>maven-surefire-plugin</artifactId>
70 <version>2.22.1</version>
71 </plugin>
72 <plugin>
73 <artifactId>maven-war-plugin</artifactId>
74 <version>3.2.2</version>
75 </plugin>
76 <plugin>
77 <artifactId>maven-install-plugin</artifactId>
78 <version>2.5.2</version>
79 </plugin>
80 <plugin>
81 <artifactId>maven-deploy-plugin</artifactId>
82 <version>2.8.2</version>
83 </plugin>
84 </plugins>
85 </pluginManagement>
86 <resources>
87 <!--指定资源目录,编译xml与propertis文件-->
88 <resource>
89 <directory>src/main/java</directory>
90 <includes>
91 <include>**/*.xml</include>
92 <include>**/*.properties</include>
93 </includes>
94 <filtering>false</filtering>
95 </resource>
96 </resources>
97 </build>
98</project>