mybatis个人总结
一、mybatis全局配置文件
(一)dtd约束
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
(二)properties引入外部文件
<!--
1、mybatis 可以使用 properties 来引入外部 properties 外部文件的内容
resource:引入类路径下资源
url:引入网络路径或着磁盘路径下的资源
-->
<properties resource="db.properties"></properties>
(三)Setting运行行为设置
<!--
2、settings 包含了很多重要的设置项
setting 用来设置每一个设置项
name 设置项名 (详情查看mybatis文档->Configuration->settings)
value 设置项取值
-->
<settings>
<!-- mapUnderscoreToCamelCase 驼峰命名规范映射javabean风格(last_name -> lastName) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 延时加载 -->
<!-- 显示的指定的我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 -->
<!-- 懒加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 在开启懒加载的情况下,true 随时加载(查询2次) false 按需加载(查询1次) -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
(四)plugin 插件
<!-- plugins 插件注册
interceptor 实现了Interceptor 接口的插件类
-->
<plugins>
<plugin interceptor="com.mybatis.plugin.plugins.MyFristPlugin">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</plugin>
<plugin interceptor="com.mybatis.plugin.plugins.MySecondPlugin">
<property name="username" value="root2"/>
<property name="password" value="654321"/>
</plugin>
</plugins>
(五)TypeAliases别名处理器
<!--
3、typeAliases 别名处理器:为java类型取别名(在映射配置文件中使用)
别名不区分大小写
-->
<typeAliases>
<!--
1)方式一:使用 typeAlias 为类起别名
type 要取别名类的全类名
alias 该类取的别名
-->
<!-- <typeAlias type="com.mybatis.test.helloworld.Student" alias="stu"></typeAlias>-->
<!--
2)方式二:使用 package 为某个包下的所有类批量取别名
name 指定包名,为当前包以及下面所有子包的每一个类都起别名,别名默认为该类类名(不区分大小写)
-->
<package name="com.mybatis.test.helloworld"/>
<!--
3)方式三:在使用 package 下,使用@@Alias注解可以为该注解修饰的类取别名
-->
</typeAliases>
(六)environments 环境配置
<!--
4、environments 环境,mybatis可以配置多种环境,defualt指定使用某种环境。可以达到快速切换环境的效果
environment 配置一个具体的环境信息:必须有两个标签,id代表该环境的唯一标识
transactionManager 事务管理器
type 事务管理器的类型(一般用spring来进行事务管理)
JDBC(JdbcTransactionFactory)|MANAGED(ManagedTransactionFactory) Configuration
自定义事务管理器:实现 TransactionFactory 接口,type指定为全类名
dataSource 数据源
type 数据源类型
UNPOOLED(UnpooledDataSourceFactory) 非数据库连接池
|POOLED(PooledDataSourceFactory) 数据库连接池
|JNDI(JndiDataSourceFactory)
自定义数据源:实现 DataSourceFactory 接口,type为全类名
-->
<environments default="dev_default">
<environment id="dev_default">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="dev_mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
(七)databaseIdProvider 支持多数据库厂商
<!--
5、databaseIdProvider 支持多数据库厂商
type="DB_VENDOR" (VendorDatabaseIdProvider)
作用得到数据库厂商标识(驱动getDatabaseProductName() ),mybais就能够根据数据库厂商标识来执行不同的sql
-->
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商取别名 -->
<property name="MySQL" value="mysql"/>
</databaseIdProvider>
(八)mappers sql映射文件
<!-- 将写好的sql映射文件一定要注册到全局配置文件中 -->
<!-- 6、mappers 将sql映射文件注册到全局配置文件中 -->
<mappers>
<!--
mapper 注册一个sql映射文件
1) 方式一:利用resource属性应用类路径下的sql文件
2) 方式二:利用url属性利用网络路径或磁盘路径下的sql文件
3) 方式三:注册接口 class属性
条件:
1> 有sql映射文件,映射文件必须和接口同名,并且放在与接口同一目录下
2> 没有sql映射文件,所有的sql都利用注解写在接口上
推荐:比较重要的复杂的Dao接口用sql映射文件
不重要,简单的Dao接口为了快速开发可以使用注解
-->
<!-- <mapper resource="studentMapper.xml"/>-->
<!-- <mapper class="com.mybatis.test.helloworld.dao.StudentAnnocationMapper"></mapper>-->
<!-- package 批量注册sql -->
<!--
批量注册的要求与用mapper的class属性的要求一样需要mapper映射文件与类同名,并且在同一包下
-->
<package name="com.mybatis.test.helloworld.dao"/>
</mappers>
二、mybatis 接口式编程
(一)接口式编程
/**
* 1、接口式编程
* 原生: Dao ==> DaoImpl
* mybatis Mapper ==> xxMapper.xml
*
* 2、SqlSession代表和数据库的一次会话,用完必须关闭。
* 3、SqlSession和connection一样都是线程不安全的。每次使用都应该获取新的对象。
* 4、mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。
* 5、两个重要的配置文件:
* mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等。。。系统运行环境信息
* sql映射文件:保存了每个sql语句的映射信息。
*/
(二)mybatis执行
1、旧版mybatis用法
/**
* 旧版本的用法
* 1、根据xml配置文件(全局配置文件)创建一个 SqlSessionFactory 对象
* 2、sql映射文件:配置每一个sql,以及sql的封装规则等
* 3、将sql映射文件注册到全局配置文件中
* 4、写代码
* 1)根据全局配置文件得到SqlSessionFactory
* 2)使用SqlSessionFactory获取到sqlSession对象使用它来执行增删改查
* 一个sqlSession代表和数据库会话一次,用完关闭
* 3)使用sql的唯一标志来告诉mybatis执行那个sql,sql都是保存在sql映射文件当中
*/
@Test
public void testMybatis(){
//1、根据xml配置文件(全局配置文件)创建一个 SqlSessionFactory 对象
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、获取 SqlSession 实例,能直接执行以映射的sql语句
SqlSession session = sqlSessionFactory.openSession();
try {
//selectOne("全类名.方法",参数[])
Student student = session.selectOne("com.mybatis.test.helloworld.StudentMapper.selectStudent", 55);
System.out.println(student);
} finally {
session.close();
}
}
2、现在常用(接口式编程)
//获取SqlSessionFactory的方法
public SqlSessionFactory getSqlSessionFactory(){
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
/**
* 常用
* 通过接口实现方法在映射配置文件中配置接口中相对应的方法来实现映射
*/
@Test
public void testInterfaceMapper(){
//1、获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2、获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3、获取接口实现类对象
//会为接口自动创建一个代理对象,代理对象去执行增删改查方法
try{
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
System.out.println(mapper.getClass());
Student student = mapper.getStudentById(2);
System.out.println(student);
}finally {
sqlSession.close();
}
}
三、mybatis的增删改查(sql都写在mapper中)
//studentMapper
public interface StudentMapper {
public Student getStudentById(Integer id);
public Long addStu(Student student);
public Boolean updateStuById(Student student);
public void deleteStuById(Integer id);
// public Student selectStuByIdAndName(Integer id,String name);
//通过注解命名key
public Student selectStuByIdAndName(@Param("id") Integer id, @Param("name") String name);
public Student selectStuByMap(Map<String,Object> map);
public List<Student> selectStuList(String name);
public Map<String,Object> selectStuByIdReturnMap(Integer id);
//通过 @MapKey 注解告诉map的key是那个主键
@MapKey("id")
public Map<Integer,Student> selectStuLikeReturnMap(String name);
}
(一)查询
1、单参数
<!--
resultType 方法返回值类型 由于之前在全局配置中使用
<typeAlias type="com.mybatis.test.helloworld.Student" alias="stud"></typeAlias>
给Student类起了别名
通过id和接口方法名一致进行方法绑定
public Student getStudentById(Integer id);
-->
<select id="getStudentById" resultType="stud">
<!-- 若对应类属性名与数据库中属性名不一致则用别名命名 -->
select id,sno,sname name from studata where id = #{id}
</select>
2、多参数
<!-- 多个参数查询
/**
* 多个参数:mybatis 会做特殊处理。
* 多个参数会被封装成 Map ,
* Key:默认是param1.。。paramN,或者参数索引(0.。。。。n)
* value:传入的参数
* #{}就是从 map 中获取 key 对应的值
*
* 命名参数:明确指定封装时参数指定的key,通过使用 @Param 注解来命名key的值
*/
public Student selectStuByIdAndName(Integer id,String name);
-->
<select id="selectStuByIdAndName" resultType="com.mybatis.test.helloworld.Student">
<!-- select id,sno,sname name from studata where id=#{param1} and sname=#{param2} -->
select id,sno,sname name from studata where id=#{id} and sname=#{name}
</select>
3、pojo类参数
<!--
parameterType:执行参数类型(可以不用写)
获取自增主键的值:
mybatis 支持主键自增,自增主键值的获取:mybatis 也是利用statement.getGenreatedKeys();
useGeneratedKeys="true" 使用自增主键获取值的策略
keyProperty="id" 指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javabean的属性
-->
<insert id="addStu" parameterType="com.mybatis.test.helloworld.Student" useGeneratedKeys="true" keyProperty="id">
insert into studata(sno,sname) values(#{sno},#{name})
</insert>
(二)添加
<!-- public void addStu(Student student); -->
<!--
parameterType:执行参数类型(可以不用写)
获取自增主键的值:
mybatis 支持主键自增,自增主键值的获取:mybatis 也是利用statement.getGenreatedKeys();
useGeneratedKeys="true" 使用自增主键获取值的策略
keyProperty="id" 指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javabean的属性
-->
<insert id="addStu" parameterType="com.mybatis.test.helloworld.Student" useGeneratedKeys="true" keyProperty="id">
insert into studata(sno,sname) values(#{sno},#{name})
</insert>
<insert id="test">
<!-- 获取非自增主键的值
keyProperty 返回方法对应的属性
order 优先级AFTER、BEFORE在sql语句执行前执行还是执行后执行sql2语句
resultType 返回结果的类型
-->
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
<!-- sql2语句 -->
</selectKey>
<!-- sql语句 -->
</insert>
(三)更新
<!--public void updateStuById(Student student);-->
<update id="updateStuById">
update studata set sno=#{sno},sname=#{name} where id=#{id}
</update>
(四)删除
<!--public void deleteStuById(Integer id);-->
<delete id="deleteStuById">
delete from studata where id=#{id}
</delete>
四、参数处理
(一)List集合
<!-- public List<Student> selectStuList(String name); -->
<!-- resultType 还是要写集合中元素的类型 -->
<select id="selectStuList" resultType="com.mybatis.test.helloworld.Student">
select id,sno,sname name from studata where sname like #{name}
</select>
(二)map
/**
*
* 如果是多个参数后面一个参数是pojo类 public void getStu(Integer id,Student stu)
* key:param1 param2.id
* value:#{param1} #{param2.id}
*
* 如果时Collection(List,Set)类型或着是数组,也会特殊处理
* key:Collection(collection) List(list) 数组(array)
* value:#{collection[0]} #{list[0]} #{array[0]}
*/
<!-- public Map<String,Object> selectStuByIdReturnMap(Integer id);
resultType的map mybatis已经将常用的类自定义别名了
-->
<select id="selectStuByIdReturnMap" resultType="map">
select id,sno,sname name from studata where id = #{id}
</select>
<!--
@MapKey("id")
public Map<Integer,Student> selectStuLikeReturnMap(String name);
-->
<select id="selectStuLikeReturnMap" resultType="com.mybatis.test.helloworld.Student">
select id,sno,sname name from studata where sname like #{name}
</select>
五、ResultMap配置(在mapper.xml中)
/* Student类的属性
private String sno;
private String sname;
private String ssex;
private Integer sage;
private String sdept;
private List<Course> courses;
// private Course course;
*/
public interface StudentMapper {
public Student selectStuAndCourseBySnoAndCno(String sno, String cno);
public Student selectStuAndCourseBySno(String sno);
public Student selectStuAndCourseListBySno(String sno);
public Student selectStudentCourseListBySnoSecond(String sno);
public Student testDiscriminator(String sno);
}
/*Course类的属性
private String cno;
private String cname;
private Integer credit;
private Integer semester;
*/
public interface CourseMapper {
public Course selectCourseByCno(String cno);
public List<Course> selectCourseListBySno(String sno);
}
(一)resultmap标签的使用
<!--
自定义一个javabean封装规则
type 指定规则的java类型
id 唯一id方便引用
-->
<resultMap id="resultmap_01" type="com.mybatis.test.helloworld.Student">
<!--
column 指定那一列
property 指定对应的javabean属性
-->
<id column="id" property="id"></id>
<!-- 定义普通列封装规则 -->
<result column="sno" property="sno"></result>
<result column="sname" property="name"></result>
<!-- 其他不指定的列会自动封装规则 -->
</resultMap>
<!--
resultMap 自定义结果集映射规则
-->
<select id="selectStuByIdPlus" resultMap="resultmap_01">
select id,sno,sname name from studata where id = #{id}
</select>
(二)联合查询封装结果集
<!-- 联合查询:级联属性封装结果集 -->
<resultMap id="StuAndCourse" type="com.mybatis.test.bean.Student">
<id column="sno" property="sno"></id>
<result column="sname" property="sname"></result>
<result column="ssex" property="ssex"></result>
<result column="sage" property="sage"></result>
<result column="sdept" property="sdept"></result>
<result column="cno" property="course.cno"></result>
<result column="cname" property="course.cname"></result>
<result column="credit" property="course.credit"></result>
<result column="semester" property="course.semester"></result>
</resultMap>
(三)分布查询封装结果集
<!-- 分布查询 -->
<resultMap id="StuAndCourse_02" type="com.mybatis.test.bean.Student">
<id column="sno" property="sno"></id>
<result column="sname" property="sname"></result>
<result column="ssex" property="ssex"></result>
<result column="sage" property="sage"></result>
<result column="sdept" property="sdept"></result>
<!--
association 子标签可以指定联合的JavaBean对象
javaType 指定属性对象的类型,不可省略
-->
<association property="course" javaType="com.mybatis.test.bean.Course">
<id column="cno" property="cno"></id>
<result column="cname" property="cname"></result>
<result column="credit" property="credit"></result>
<result column="semester" property="semester"></result>
</association>
</resultMap>
<!-- StudentMapper.xml -->
<resultMap id="StuAndCourse_03" type="com.mybatis.test.bean.Student">
<id column="sno" property="sno"></id>
<result column="sname" property="sname"></result>
<result column="ssex" property="ssex"></result>
<result column="sage" property="sage"></result>
<result column="sdept" property="sdept"></result>
<!-- association 定义关联对象的封装规则
select 表明当前属性是调用select指定的方法查出的结果
column 指定将那一列值传给这个方法
调用CourseMapper的方法查出属性course的数据
-->
<association property="course" select="com.mybatis.test.dao.CourseMapper.selectCourseByCno" column="cno">
</association>
</resultMap>
<!-- CourseMapper.xml -->
<select id="selectCourseByCno" resultMap="courseMap_01">
select cno,cname,credit,semester from course where cno = #{cno}
</select>
(四)关联集合类型的两种查询方式
<!-- 联合查询 -->
<resultMap id="StuAndCourse_04" type="com.mybatis.test.bean.Student">
<id column="sno" property="sno"></id>
<result column="sname" property="sname"></result>
<result column="ssex" property="ssex"></result>
<result column="sage" property="sage"></result>
<result column="sdept" property="sdept"></result>
<!-- 关联集合类型
property (在该类中的)属性名
ofType 属性类型
-->
<collection property="courses" ofType="com.mybatis.test.bean.Course">
<id column="cno" property="cno"></id>
<result column="cname" property="cname"></result>
<result column="credit" property="credit"></result>
<result column="semester" property="semester"></result>
</collection>
</resultMap>
<!-- 分布查询 -->
<resultMap id="StuAndCourse_05" type="com.mybatis.test.bean.Student">
<id column="sno" property="sno"></id>
<result column="sname" property="sname"></result>
<result column="ssex" property="ssex"></result>
<result column="sage" property="sage"></result>
<result column="sdept" property="sdept"></result>
<!-- 关联集合类型 -->
<!--
扩展:
如果要传入多列的值用 column="{key1=column1,key2=column2}"
fetchType 是否开启懒加载
-lazy 为开启
-eager 关闭
-->
<collection property="courses" select="com.mybatis.test.dao.CourseMapper.selectCourseListBySno" column="sno" fetchType="lazy">
</collection>
</resultMap>
(五)鉴别器discriminator
<!--
discriminator 鉴别器
mybatis可以使用discriminator判断某列的值,然后根据某列的值来改变封装行为
-->
<resultMap id="discriminatorTest" type="com.mybatis.test.bean.Student">
<!--
column 指定判定的列名
javaType 列值对应的java类型
-->
<discriminator javaType="string" column="sno">
<!-- 指定学号为0811101的学生用resultMap封装,0831102的学生用其中内容封装 -->
<case value="0811101" resultMap="StuAndCourse_05">
</case>
<case value="0831102" resultType="com.mybatis.test.bean.Student">
<id column="sno" property="sno"></id>
<result column="sname" property="sname"></result>
<result column="ssex" property="ssex"></result>
<result column="sage" property="sage"></result>
<result column="sdept" property="sdept"></result>
</case>
</discriminator>
</resultMap>
六、动态sql
(一)if判断
<!--
查询时候如果某些条件没带,可能sql拼装会有问题
解决方法:
1、给 where后面加上1=1,以后的拼装条件都and xxx
2、mybatis 用where标签来将所有查询条件包括在内,
mybatis会将where标签中拼装的sql,多出来的and或or去掉
注意:mybatis 只会去掉头一个and或or
public Student selectStudentByAll(@Param("sno") String sno, @Param("sname") String sname, @Param("ssex") String ssex, @Param("sage") Integer sage, @Param("sdept") String sdept);
-->
<select id="selectStudentByAll" resultMap="Studentmap">
select sno,sname,ssex,sage,sdept
from student
where
<!-- test 判断条件 true执行拼串 false不拼其中sql -->
<if test="sno != null && sno.trim() != """>
sno = #{sno}
</if>
<if test="sname != null && sname.trim() != """>
and sname = #{sname}
</if>
<if test="ssex != null && ssex.trim() != """>
and ssex = #{ssex}
</if>
<if test="sage != null && sage != 0">
and sage = #{sage}
</if>
<if test="sdept != null && sdept.trim() != """>
and sdept = #{sdept}
</if>
</select>
(二)where查询条件 choose_when选择执行
<!-- public List<Student> selectStudentByAllChoose(@Param("sno") String sno, @Param("sname") String sname, @Param("ssex") String ssex, @Param("sage") Integer sage, @Param("sdept") String sdept); -->
<select id="selectStudentByAllChoose" resultMap="Studentmap">
select sno,sname,ssex,sage,sdept
from student
<where>
<choose>
<!-- test 条件
注:其中可以执行属性的方法
-->
<when test="sno != null && sno.trim() != """>
sno like #{sno}
</when>
<when test="sname != null && sname.trim() != """>
and sname = #{sname}
</when>
<when test="ssex != null && ssex.trim() != """>
and ssex = #{ssex}
</when>
<when test="sage != null && sage != 0">
and sage = #{sage}
</when>
<when test="sdept != null && sdept.trim() != """>
and sdept = #{sdept}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
(三)trim
<!-- public Student selectStudentByAllTrim(@Param("sno") String sno, @Param("sname") String sname, @Param("ssex") String ssex, @Param("sage") Integer sage, @Param("sdept") String sdept);
-->
<select id="selectStudentByAllTrim" resultMap="Studentmap">
select sno,sname,ssex,sage,sdept
from student
<!-- 后面多出的 and或者or不能解决 where标签不能解决
trim标签
prefix 前缀 trim标签中整个字符串拼串后的结果之前加一个前缀
prefixOverrides 前缀覆盖 去掉整个字符串前面多余的字符
suffix 后缀 trim标签中整个字符串拼串后的结果之前加一个后缀
suffixOverrides 后缀覆盖 去掉整个字符串后面多余的字符
-->
<trim prefix="where" suffixOverrides="and">
<if test="sno != null && sno.trim() != """>
sno = #{sno} and
</if>
<if test="sname != null && sname.trim() != """>
sname = #{sname} and
</if>
<if test="ssex != null && ssex.trim() != """>
ssex = #{ssex} and
</if>
<if test="sage != null && sage != 0">
sage = #{sage} and
</if>
<if test="sdept != null && sdept.trim() != """>
sdept = #{sdept} and
</if>
</trim>
</select>
(四)set_if结合
<!-- public Integer updateStuByAllSet(@Param("sno") String sno, @Param("sname") String sname, @Param("ssex") String ssex, @Param("sage") Integer sage, @Param("sdept") String sdept);
-->
<update id="updateStuByAllSet">
update student
<set>
<if test="sname != null && sname.trim() != """>
sname = #{sname},
</if>
<if test="ssex != null && ssex.trim() != """>
ssex = #{ssex},
</if>
<if test="sage != null && sage != 0">
sage = #{sage},
</if>
<if test="sdept != null && sdept.trim() != """>
sdept = #{sdept},
</if>
</set>
<where>
sno = #{sno}
</where>
</update>
(五)foreach遍历
<!-- public List<Student> selectStudentByAllForeach(@Param("snoList") List<String> snoList);
-->
<select id="selectStudentByAllForeach" resultMap="Studentmap">
select sno,sname,ssex,sage,sdept
from student
<where>
sno in
<!--
collection 指定要遍历的集合
list类型的参数会做特殊处理封装为map中,map的key就叫list
item 将当前遍历出的元素赋值给指定的变量
separator 每一个元素之间的分隔符
open 遍历出所有结果拼接开始的字符
close 遍历出所有结果拼接结束的字符
index
遍历list时候index就是索引,item是当前值
遍历map时候index表示的就是map的key,item就是map的value
-->
<foreach collection="snoList" item="snoid" separator="," open="(" close=")" index="">
#{snoid}
</foreach>
</where>
</select>
(六)foreach批量插入两种方式
1、方式一
<!-- 批量插入
注:mysql数据库支持该 insert into student(sno,sname,ssex,sage,sdept) values(#{stu.sno},#{stu.sname},#{stu.ssex},#{stu.sage},#{stu.sdept}),……的方式
-->
<insert id="insertStus">
insert into student(sno,sname,ssex,sage,sdept)
values
<foreach collection="stuList" item="stu" separator=",">
(#{stu.sno},#{stu.sname},#{stu.ssex},#{stu.sage},#{stu.sdept})
</foreach>
</insert>
2、方式二
<!--
这种链接方式需要数据库链接属性allowMultiQueries = true(全局配置中设置)
这种分号分隔多个sql可以用于其他的批量操作
-->
<insert id="insertStus2">
<foreach collection="stuList" item="stu" separator=";">
insert into student(sno,sname,ssex,sage,sdept)
values (#{stu.sno},#{stu.sname},#{stu.ssex},#{stu.sage},#{stu.sdept})
</foreach>
</insert>
(七)sql内置参数 _parameter和 _databaseId 以及 bind的使用
<!--
两个内置参数:
不只是方法传递过来的参数可以判断取值
mybatis 默认还有两个内置参数:
_parameter:代表整个参数
单个参数 _parameter就是参数
多个参数 参数会被封装为一个map,_parameter代表这个map
_databaseId:如果配置了databaseIdProvider标签
_databaseId就是代表当前数据库别名。
-->
<!--
bind标签
可以将一个ognl表达式的值绑定到一个变量中,方便后来应用这个变量的值
-->
<select id="selectStuByName" resultType="com.mybatis.dynamicsql.bean.Student">
<bind name="name" value="'%'+_parameter+'%'"/>
select sno,sname,ssex,sage,sdept from student
<if test="_parameter != null">
<!-- #{name} 即为bind的value值 -->
where sname like #{name}
</if>
</select>
(八)可重用sql片段
<!--
抽取可重用sql片段。方便后面引用
1、sql抽取经常要查询列名,或者插入用的列名抽取出来的方便引用
2、include来应用已经抽取的sql
3、include还可以自定义一些porperty,sql标签内部使用的自定义属性 取值方式${prop} 不能使用#{prop}
-->
<sql id="selectinto">
sno,sname,ssex,sage,sdept
</sql>
七、缓存
例图
(一)缓存配置
/** 和缓存有关的设置/属性:
* 1)、cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)
* 2)、每个select标签都有useCache="true":
* false:不使用缓存(一级缓存依然使用,二级缓存不使用)
* 3)、【每个增删改标签的:flushCache="true":(一级二级都会清除)】
* 增删改执行完成后就会清楚缓存;
* 测试:flushCache="true":一级缓存就清空了;二级也会被清除;
* 查询标签:flushCache="false":
* 如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使用的;
* 4)、sqlSession.clearCache();只是清楚当前session的一级缓存;
* 5)、localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会话缓存中;
* STATEMENT:可以禁用一级缓存;
*/
(二)一级缓存
/** 一级缓存:(本地缓存):sqlSession级别的缓存。一级缓存是一直开启的;SqlSession级别的一个Map
* 与数据库同一次会话期间查询到的数据会放在本地缓存中。
* 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
*
* 一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询):
* 1、sqlSession不同。
* 2、sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)
* 3、sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
* 4、sqlSession相同,手动清除了一级缓存(缓存清空)
*/
@Test
public void testFirstLevelCache(){
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession1 = sqlSessionFactory.openSession();
try{
StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);
Student student1 = mapper1.selectStudentByAllTrim("0811101",null,null,null,null);
System.out.println(student1);
//1、sqlSession不同。
// SqlSession sqlSession2 = sqlSessionFactory.openSession();
// StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
// Student student2 = mapper2.selectStudentByAllTrim("0811101",null,null,null,null);
// System.out.println(student2 == student1);
//2、sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)
// StudentMapper mapper2 = sqlSession1.getMapper(StudentMapper.class);
// Student student2 = mapper2.selectStudentByAllTrim("0811102", null, null, null, null);
// System.out.println(student2 == student1);
//3、sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
// StudentMapper mapper2 = sqlSession1.getMapper(StudentMapper.class);
// ArrayList<Student> students = new ArrayList<>();
// students.add(new Student("0811109", "赵九", "男", 19, "信息管理系", new ArrayList<Course>()));
// mapper2.insertStus2(students);
// System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
// Student student2 = mapper2.selectStudentByAllTrim("0811101", null, null, null, null);
// System.out.println(student2);
//4、sqlSession相同,手动清除了一级缓存(缓存清空)
sqlSession1.clearCache();
StudentMapper mapper2 = sqlSession1.getMapper(StudentMapper.class);
Student student2 = mapper2.selectStudentByAllTrim("0811101", null, null, null, null);
System.out.println(student2 == student1);
sqlSession1.commit();
// sqlSession2.commit();
// sqlSession2.close();
}finally {
sqlSession1.close();
}
}
(三) 二级缓存
/** 二级缓存:(全局缓存):基于namespace级别的缓存:一个namespace对应一个二级缓存:
* 工作机制:
* 1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
* 2、如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;
* 3、sqlSession===EmployeeMapper==>Employee
* DepartmentMapper===>Department
* 不同namespace查出的数据会放在自己对应的缓存中(map)
* 效果:数据会从二级缓存中获取
* 查出的数据都会被默认先放在一级缓存中。
* 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
* 使用:
* 1)、开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>
* 2)、去mapper.xml中配置使用二级缓存:
* <cache></cache>
* 3)、我们的POJO需要实现序列化接口
*/
@Test
public void testSecondCache(){
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
//只有sqlSession关闭数据才会存到二级缓存中
Student student1 = mapper.selectStuBySno("0811101");
System.out.println(student1);
sqlSession1.close();
Student student2 = mapper2.selectStuBySno("0811101");
System.out.println(student2);
System.out.println(student1==student2);//结果为true(student2更student1查询条件一样,student2是在关闭数据库时存储在二级缓存中的对象即student1)
}
(四)第三方缓存
1、三方缓存配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\44\ehcache" />
<defaultCache
maxElementsInMemory="10000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
<!--
属性说明:
l diskStore:指定数据在磁盘中的存储位置。
l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的:
l maxElementsInMemory - 在内存中缓存的element的最大数目
l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
-->
2、三方缓存在mapper中注册
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
<!--
eviction:缓存的回收策略:
• LRU – 最近最少使用的:移除最长时间不被使用的对象。
• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
• SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
• WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
• 默认的是 LRU。
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读:
true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
false:非只读:mybatis觉得获取的数据可能会被修改。
mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
size:缓存存放多少元素;
type="":指定自定义缓存的全类名;
实现Cache接口即可;
-->
3、三方缓存执行
/**第三方缓存整合:
* 1)、导入第三方缓存包即可;
* 2)、导入与第三方缓存整合的适配包;官方有;
* 3)、mapper.xml中使用自定义缓存
* <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
*/
@Test
public void testThreeCache(){
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
Student student1 = mapper.selectStuBySno("0811101");
System.out.println(student1);
sqlSession1.close();
Student student2 = mapper2.selectStuBySno("0811101");
System.out.println(student2);
sqlSession2.close();
System.out.println(student1==student2);
}
八、ssm粗略整合
(一)ssm整合步骤
1、导包
1)IOC容器包
2)AOP功能包
3)JDBC事物包
4)SpringMVC包
5)JSTL包
6)mybatis-spring包
7)日志包
2、web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- spring配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- springMVC配置 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3、SpringMVC配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 只扫描控制器 -->
<context:component-scan base-package="com.mybatis.ssm" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 开启springmvc注解 -->
<mvc:annotation-driven></mvc:annotation-driven>
<mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>
4、Spring配置文件(mybatis的事物控制模块)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
<context:component-scan base-package="com.mybatis.ssm">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 导入外部配置文件 -->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- Spring用来控制业务逻辑。数据源、事务控制、aop -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启基于注解的事务 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!-- 整合mybatis
目的:
1、Spring 管理所有组件。mapper的实现类。
Service ===>DAO @AutoWired:自动注入mapper
2、Spring 用来管理事务,spring声明事务
-->
<!-- 创建出SqlSessionFactory对象 -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!-- configLocation 指定全局配置文件的位置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 指定mapper文件的位置 -->
<!-- <property name="mapperLocations" value="classpath:com/mybatis/ssm/dao/*.xml"></property>-->
</bean>
<!-- 配置一个可以进行批量执行的sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>
<!-- 扫描所有的mapper接口实现,让这些mapper能够自动注入 必须和dao接口同一个包下-->
<mybatis-spring:scan base-package="com.mybatis.ssm.dao"></mybatis-spring:scan>
<!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">-->
<!-- <property name="basePackage" value="com.mybatis.ssm.dao"></property>-->
<!-- </bean>-->
</beans>
5、mybatis全局配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- mapUnderscoreToCamelCase 驼峰命名规范映射javabean风格(last_name -> lastName) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 延时加载 -->
<!-- 显式的指定的我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 -->
<!-- 懒加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 在开启懒加载的情况下,true 随时加载(查询2次) false 按需加载(查询1次) -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
6、mybatis局部配置文件
7、控制器
@Controller
public class StudentController {
@Autowired
StudentService studentService;
@RequestMapping("/getStus")
public String stus(Map<String,Object> map){
List<Student> stus = studentService.getStu();
map.put("allStus",stus);
return "/list";
}
}
8、服务器
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
//这个sqlSession可以批量操作
@Autowired//自动填装在配置文件中写好的可批量操作的SqlSession对象
private SqlSession sqlSession;
public List<Student> getStu(){
return studentMapper.selectStuAll();
}
}
9、页面
<!DOCTYPE html>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<meta charset="UTF-8">
<head>
<title>$Title$</title>
</head>
<body>
<a href="getStus">查询所有学生</a>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<table>
<c:forEach items="${allStus}" var="stu">
<tr>
<td>${stu.sno}</td>
<td>${stu.sname}</td>
<td>${stu.ssex}</td>
<td>${stu.sage}</td>
<td>${stu.sdept}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
九、逆向工程mbg
(一)mbg配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime 用于指定生成模式
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- jdbcConnection 指定如何链接目标数据库 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql:///test_db?characterEncoding=utf-8&serverTimezone=UTC&rewriteBatchedStatements=true&allowMultiQueries=true"
userId="root"
password="123456">
</jdbcConnection>
<!-- javaTypeResolver 定义属性如何使用java解析器 -->
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- javaModelGenerator 指定javabean的生成策略
targetPackage 目标包名
targetProject 目标工程
-->
<javaModelGenerator targetPackage="com.mybatis.mbg.bean" targetProject=".\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- sqlMapGenerator sql映射文件生成策略 -->
<sqlMapGenerator targetPackage="com.mybatis.mbg.dao" targetProject=".\conf">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- javaClientGenerator 指定mapper接口所在位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.mybatis.mbg.dao" targetProject=".\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- table 指定要你想分析那些表 根据表来创建javaBean
domainObjectName 指定要创建的javabean类叫什么名字
-->
<table tableName="student" domainObjectName="Student"></table>
<table tableName="sc" domainObjectName="Sc"></table>
<table tableName="course" domainObjectName="Course"></table>
</context>
</generatorConfiguration>
(二)mbg执行
@Test
public void testMBG() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("conf/mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
(三)执行结果(结果均为使用targetRuntime=“MyBatis3”)
十、myabtis运行原理
总描述
/**
* 1、获取sqlSessionFactory对象:
* 解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;
* 注意:【MappedStatement】:代表一个增删改查的详细信息
*
* 2、获取sqlSession对象
* 返回一个DefaultSQlSession对象,包含Executor和Configuration;
* 这一步会创建Executor对象;
*
* 3、获取接口的代理对象(MapperProxy)
* getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象
* 代理对象里面包含了,DefaultSqlSession(Executor)
* 4、执行增删改查方法
*
* 总结:
* 1、根据配置文件(全局,sql映射)初始化出Configuration对象
* 2、创建一个DefaultSqlSession对象,
* 他里面包含Configuration以及
* Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
* 3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
* 4、MapperProxy里面有(DefaultSqlSession);
* 5、执行增删改查方法:
* 1)、调用DefaultSqlSession的增删改查(Executor);
* 2)、会创建一个StatementHandler对象。
* (同时也会创建出ParameterHandler和ResultSetHandler)
* 3)、调用StatementHandler预编译参数以及设置参数值;
* 使用ParameterHandler来给sql设置参数
* 4)、调用StatementHandler的增删改查方法;
* 5)、ResultSetHandler封装结果
* 注意:
* 四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);
*
* @throws IOException
*/
@Test
public void test01() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper);
System.out.println(employee);
} finally {
openSession.close();
}
}
(一)根据配置文件创建sqlSessionFactory对象
以下图片均出自于尚硅谷
1、Configuration对象
Configuration对象保存了所有配置文件的详细信息
2、创建DefaultSqlSessionFactory的流程
(二)创建sqlSession
(三)sqlSession获取Mapper
(四)查询
十一、插件
(一)插件类
/**
* 实现Intercetpor接口
* 完成插件签名
* 告诉mybatis当前插件用来拦截那个对象的那个方法
*/
@Intercepts(
{ //type 拦截哪个类 method 拦截那个方法 args 这个方法的参数
@Signature(type = StatementHandler.class,method = "parameterize",args = Statement.class)
}
)
public class MyFristPlugin implements Interceptor {
/**
* 拦截目标对象的目标方法的执行
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
//动态改变sql运行参数:查询0811102的学生
Object target = invocation.getTarget();
System.out.println("以前拦截到的对象:"+target);
//StatementHandle==>ParameterHandle==>ParamterObject
//拿到target的元数据
MetaObject metaObject = SystemMetaObject.forObject(target);
Object value = metaObject.getValue("parameterHandler.parameterObject");
System.out.println("sql的参数是:"+value);
metaObject.setValue("parameterHandler.parameterObject","0811103");
//执行目标方法
Object proceed = invocation.proceed();
System.out.println("MyFristPlugin...intercept:"+invocation.getMethod());
//返回目标方法
return proceed;
}
/**
* 包装目标对象,即为目标对象创建一个代理对象
* @param o
* @return
*/
@Override
public Object plugin(Object o) {
//可以借助Plugin的wrap方法来使用当前Interceptor包装我们的目标对象
Object wrap = Plugin.wrap(o, this);
System.out.println("MyFristPlugin...plugin:mybatis将要包装的对象:"+o);
//放回当前o创建的动态代理
return wrap;
}
/**
* 将当前插件注册时的properties属性设置进来
* @param properties
*/
@Override
public void setProperties(Properties properties) {
System.out.println("插件配置信息:"+properties);
}
}
(二)插件注册
在mybatis的全局配置文件中setting标签后注册
<!-- plugins 插件注册 -->
<plugins>
<plugin interceptor="com.mybatis.plugin.plugins.MyFristPlugin">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</plugin>
<plugin interceptor="com.mybatis.plugin.plugins.MySecondPlugin">
<property name="username" value="root2"/>
<property name="password" value="654321"/>
</plugin>
</plugins>
(三)多个插件运行
以下图片均出自于尚硅谷
十二、扩展
(一)PageHelper插件的使用
需导入PageHelper jar包
1、注册
<!-- plugins 插件注册 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
2、运行
@Test
public void testPageHelper(){
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Page<Object> page = PageHelper.startPage(3, 3);
List<Student> students = mapper.selectAllStu();
//连续要显示多少页
PageInfo<Student> pageInfo = new PageInfo<>(students,2);
for(Student stu:students){
System.out.println(stu);
}
System.out.println();
System.out.println("当前页码:"+page.getPageNum());
System.out.println("总记录数:"+page.getTotal());
System.out.println("每页记录数:"+page.getPageSize());
System.out.println("总页码:"+page.getPages());
System.out.println();
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println();
System.out.println("连续显示的页码:");
int[] nums = pageInfo.getNavigatepageNums();
for (int i = 0 ; i < nums.length ; i ++ )
System.out.println(nums[i]);
sqlSession.commit();
}finally {
sqlSession.close();
}
}
(二)批量操作
获取可批量操作的sqlSession
@Test
public void testBatch(){
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//获取一个能执行批量插入的SqlSession
//time ==》1861
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
//获取普通插入的SqlSession
//time ==》8389
// SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
long start = System.currentTimeMillis();
try{
for (int i = 1 ; i <= 10000 ; i++){
Random random = new Random();
String sno = String.valueOf(i);
String sname = String.valueOf(random.nextInt(10000));
mapper.addStu(new Student(sno,sname,"男",12,"计算机系"));
}
sqlSession.commit();
long end = System.currentTimeMillis();
System.out.println(end-start);
}finally {
sqlSession.close();
}
}
(三)自定义TypeHandler
1、注册
<typeHandlers>
<!-- 1、配置自定义的类型处理器 -->
<typeHandler handler="com.mybatis.pageHelper.typeHandler.MyEmpTypeHandler" javaType="com.mybatis.pageHelper.Enum.EmpStatus"></typeHandler>
</typeHandlers>
2、TypeHandler实现类
public class MyEmpTypeHandler implements TypeHandler<EmpStatus> {
/**
* 参数处理
* @param preparedStatement 预编译流
* @param i 参数对应的占位符
* @param empStatus 枚举对象
* @param jdbcType jdbc类型
* @throws SQLException
*/
@Override
public void setParameter(PreparedStatement preparedStatement, int i, EmpStatus empStatus, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i,empStatus.getCode().toString());
}
/**
* 根据数据库中返回的状态码返回一个枚举对象
* @param resultSet 结果集
* @param s 状态码
* @return 当前状态
* @throws SQLException
*/
@Override
public EmpStatus getResult(ResultSet resultSet, String s) throws SQLException {
int code = resultSet.getInt(s);
return EmpStatus.getEmpStatusByCode(code);
}
@Override
public EmpStatus getResult(ResultSet resultSet, int i) throws SQLException {
int code = resultSet.getInt(i);
return EmpStatus.getEmpStatusByCode(code);
}
@Override
public EmpStatus getResult(CallableStatement callableStatement, int i) throws SQLException {
int code = callableStatement.getInt(i);
return EmpStatus.getEmpStatusByCode(code);
}
}
3、枚举类
public enum EmpStatus {
LOGIN(100,"登入"),LOGOUT(200,"离线"),REMOVE(300,"无此用户");
private Integer code;
private String msg;
EmpStatus(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public static EmpStatus getEmpStatusByCode(Integer code){
switch (code){
case 100: return LOGIN;
case 200: return LOGOUT;
case 300: return REMOVE;
default: return LOGOUT;
}
}
}
4、执行
@Test
public void testEnum(){
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> employeeList = new ArrayList<>();
employeeList.add(new Employee(null,"张三", EmpStatus.getEmpStatusByCode(100)));
employeeList.add(new Employee(null,"张四", EmpStatus.getEmpStatusByCode(200)));
employeeList.add(new Employee(null,"张五", EmpStatus.getEmpStatusByCode(300)));
employeeList.add(new Employee(null,"张六", EmpStatus.getEmpStatusByCode(200)));
employeeList.add(new Employee(null,"张七", EmpStatus.getEmpStatusByCode(100)));
try{
for(Employee emp : employeeList){
mapper.addEmp(emp);
}
sqlSession.commit();
}finally {
sqlSession.close();
}
System.out.println(employeeList);
}