MyBatis总结
MyBatis
MyBatis本来是apache的一个开源项目iBatis,后来由apache software foundation迁移到了google code,并改名为MyBatis。2013年11月迁移到GitHub。
MyBatis是一款优秀的持久化框架,它支持定制化SQL、储存过程以及高级映射。MyBatis几乎避免了所有的JDBC代码,和手动设置参数,以及获取结果集。
搭建MyBatis
添加驱动包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
添加配置文件:src/mybatis-config/xml
<?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>
<!--连接数据库的环境,default="环境的id"-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/chuange"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 指定maper文件的路径(maven项目从resources源文件夹下找资源)-->
<mappers>
<mapper resource="包名/mapper文件名"/>
</mappers>
</configuration>
创建实体类和接口
添加Mapper文件
<mapper namespace="接口的完整路径">
<insert id="方法名" parameterType="参数类型">
//sql
</insert>
<select id="方法名" resultType="查询后的返回值类型">
//sql语句---注:sql语句没有分号
</select>
</mapper>
修改mybatis的配置文件
修改配置文件,让该配置文件知道mapper的存在
获取SqlSession
通过该对象进行数据的操作
//1.加载配置文件
Reader r = Resources.getResourceAsReader("mybatis-config.xml");
//2.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.得到session工厂
SqlSessionFactory factory = builder.build(r); //4.得到session
SqlSession session = factory.openSession(); //5.调取sql语句,insert("方法的完整路径"),路径=namespace+id
int rs = session.insert("dao.EmpDao.insertEmp", e);
session.commit();
Mybatis实现CRUD
mapper文件中参数的读取:单个基本类型参数或者String类型参数。
mapper读取参数:#{参数名(也可以是自定义名称)}
参数类型为对象类型时,读取参数的语句:#{对象中的属性名}
insert,delete,update,select中的parameterType可以省略
多个参数的情况:将参数封装到map集合中,再将map集合传递给mapper文件,取值的时候,#{map的key值}
处理结果没有实现类的时候可以直接返回map类型
<select id="jisuan" resultType="map">
在查询的时候如果需要将查询的实体类属性自动对应的话需要保证,属性名=列名
添加:session.insert(“namespace+id”[,传递给sql的参数值]);
修改:session.update(“namespace+id”[,传递给sql的参数值]); 删除:session.delete(“namespace+id”[,传递给sql的参数值]);
单行:session.selectOne(“namespace+id”[,传递给sql的参数值]);
多行:session.selectList(“namespace+id”[,传递给sql的参数值]);
处理多个聚合函数:使用map作为方法的返回值,默认key是列名
注意:增删改的时候需要提交事务
session.commit();
session.rollback();
省略实现类
Reader r=Resources.getResourceAsReader("mybatis.xml");
SqlSession session= new SqlSessionFactoryBuilder().build(r).openSession();
//参数是接口的class类
StudentDao dao=session.getMapper(StudentDao.class);
ThreadLocal处理sqlSession
介绍:ThreadLocal其实并不是一个Thread,而是threadlocalvariable(线程局部变量),线程局部变量的功能其实非常简单,就是为每一个使用该变量的线程都提供一个副本,是Java中一种较为特殊的线程绑定机制,每一个线程都可以独立改变自己的副本,而不会和其他线程的副本发生冲突。
示例:
public class TestThreadLocal {
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
private List<String> list = new ArrayList<>();
class A extends Thread{
@Override
public void run() {
//存值
System.out.println("线程A开始存值");
threadLocal.set("threadlocal的内容");
list.add("arraylist的内容");
System.out.println("A----threadLocal = " + threadLocal.get());
}
}
class B extends Thread{
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程B开始取值");
System.out.println("A----threadLocal = " + threadLocal.get());
System.out.println("list = " + list.get(0));
}
}
public static void main(String[] args) {
TestThreadLocal test = new TestThreadLocal();
TestThreadLocal.A a = test.new A();
TestThreadLocal.B b = test.new B();
a.start();
b.start();
}
}
ThreadLocal优化SqlSession
public class SqlSessionUtil {
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();
private static SqlSessionFactory sqlSessionFactory;
static {
try {
Reader reader = Resources.getResourceAsReader("mybatis.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSession(){
SqlSession session = threadLocal.get();
if (session == null){
session = sqlSessionFactory.openSession();
threadLocal.set(session);
}
return session;
}
public static void closeSession(){
SqlSession session = threadLocal.get();
if (session != null){
session.close();
threadLocal.remove();
}
}
}
给类起别名
<!--给实体类起别名 -->
<typeAliases>
<!--
<typeAlias alias="u" type="com.yhp.bean.Users"> </typeAlias>-->
<!--指定哪些包的类可以使用别名,默认别名:类名首字母小写(实际使用的时候,全部小写也可以做结果映射) -->
<package name="bean"></package>
</typeAliases>
获得新增数据的id
<insert useGeneratedKeys="true" keyProperty="userid">
Mybatis复杂查询
in查询
foreach标签中属性说明:
item 表示集合中每一个元素进行迭代的别名,等同于c标签中的var
index 指定一个名字,用于表示在迭代过程中,每次迭代的位置,可以不写
open 表示语句什么时候开始
separator 表示每次迭代之间用什么富豪作为分隔符号。
close 表示以什么结束
collection 该属性是必须制定的
list时取值是list,数组时取值是array,map时取值是map的key值
参数是list示例:
<select id="finda" resultType="student">
select * from student where studentId in
<foreach collection="list" item="sid" open="(" close=")" separator=",">
#{sid}
</foreach>
</select>
参数是数组:
<select id="findb" resultType="student">
select * from student where studentId in
<foreach collection="array" item="sid" open="(" close=")" separator=",">
#{sid}
</foreach>
</select>
参数是map:
<select id="findc" resultType="student">
select * from student where studentId in
<foreach collection="ids" item="sid" open="(" close=")" separator=",">
#{sid}
</foreach>
</select>
模糊查询
动态sql:
<if test="属性名!=属性值">
and ename like '${属性名}'
</if>
注意:test属性中读取属性值时直接写属性名
模糊查询读取属性时使el表达式,${属性名},除以上位置外,都使用#{属性名},多个条件时使用and 和 or 拼接。如果传递过来是map类型,则test属性中写的是key
#{}:相当于占位符
#{id}:其中的id可表示输入参数的名称,如果是简单类型名称可以任意。
${}:表示拼接sql语句
区间查询:
between 开始值 and 结束值
列名 >= 开始值 and 列名 <=结束值
<if test="stu.endTime!=null and stu.endTime!=''">
and regdate <![CDATA[ <= ]]> #{stu.endTime}
</if>
resultMap:
(1)处理单表关系
通过给列起别名,让别名 = 属性名,也可以实现数据的对应
resultType = “指定返回值的类型” //当列名和属性名一致的时候使用
resultMap=“key值” // 1.当列名和属性名不一致 2.做多表查询的时候
<resultMap id="aaa" type="bean.Dept">
<!-- 可以手动指定列名和属性名的关系 ,非主键列使用result 标签,主键
列使用id 标签-->
<id property="dept_no" column="deptno"></id>
<result property="d_name" column="dname"/>
<result property="d_loc" column="loc"/>
</resultMap>
(2)处理多表关系
两表联查,一对多或者多对一。
注意:如果是单表查询的话,select中只要使用resultType设置返回类型即可。
如果是多表联查则需要使用resultMap标签来进行结果映射,存的一方是集合的话使用Collection子标签,存的是一方的话使用的是association子标签
格式举例:
一对多:
<resultMap type="" id="自定义名称">
<id property="id" column="dept_id"/><!--主键列-->
<result property="java 属性名" column="列名"/>
<collection property="属性名" ofType="java 类型">
<id property="属性名" column="列名"/>
<result property="属性名" column="列名"/>
</collection>
</resultMap>
多对一:
<resultMap type="" id="">
<id property="" column=""/>
<result property="" column=""/>
<association property="" javaType="">
<id property="" column=""/>
<result property="" column=""/>
</association>
</resultMap>
pageHelper分页
Mybatis使用两种方式进行分页处理,sql语句只需要查询数据,不实现分页处理
方式一:
Mybatis使用RowBounds对象进行分页,他是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来实现分页功能。
优缺点:
物理分页每次都需要访问数据库,逻辑分页只需要访问一次。
物理分页占内存比较小,逻辑分页所占内存比较高。
物理分页每次都是最新的,逻辑分页可能有滞后。
rowBounds实现分页示例:
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml")).openSession();
List<Users> usersList = sqlSession.selectList("com.yhp.dao.UsersDao.findall",
null, new RowBounds(0, 3));//rowBounds(开始位置,显示条数)
for (Users users : usersList) {
System.out.println(users.getUsername());
}
方式二:使用分页插件
分页插件就是使用Mybatis提供的插件接口,实现自定义分页插件,在插件的拦截方法内拦截待执行的sql语句,然后重写sql。
步骤:
- 倒入jar包
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.6</version>
</dependency>
- 在mybatis总文件中配置插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
注意:这是jar包版本为5.1之后的配置方法,5.1之前的配置方法如下
<plugins>
<!-- PageHelper4.1.6 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
- 在执行查询前设置pageHelper。startPage(当前页,每条页数)
示例:
//分页查询(注意事项:设置分页的值一定要在查询之前)
// 1.在工具类中指定页码值和显示条数
PageHelper.startPage(2, 5);
//2.调取查询的方法,得到结果集
Student student1 = new Student();
//student1.setStuname("aa");
// student1.setAddress1("昌平");
List<Student> list = dao.findall(student1);
//3.将list集合封装到PageInfo对象中
PageInfo pageInfo = new PageInfo(list);
List<Student> list2 = pageInfo.getList(); //4.得到结果
for (Student student : list2) {
System.out.println(student.getStuname());
}
System.out.println("每页显示条数:" + pageInfo.getPageSize());
System.out.println("当前页的条数:" + pageInfo.getSize());
System.out.println("总条数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("上一页:" + pageInfo.getPrePage());
System.out.println("下一页:" + pageInfo.getNextPage());
System.out.println("当前页:" + pageInfo.getPageNum());
缓存
缓存分为一级缓存和二级缓存
一级缓存:SqlSession的缓存------->自动开启
二级缓存:做到从不同的缓存中共享数据SqlSessionFactoryFactory的缓存-------->手动开启
映射配置文件中配置
<mapper namespace="接口路径">
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
</mapper>
说明:
eviction:二级缓存中,缓存对象从缓存中的移除策略,先进先出FIFO
flushInterval:刷新缓存事件间隔,单位:毫秒
size:缓存对象的个数
readOnly:是否是只读
测试代码:
//不同qlSession,要同一个sqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
SqlSession sqlSession1 = factory.openSession();
Student student = sqlSession1.selectOne("com.yhp.dao.StudentDao.findbystuid", 1);
System.out.println(student.getSname());
sqlSession1.close();
System.out.println("===================================");
SqlSession sqlSession2 = factory.openSession();
student = sqlSession2.selectOne("com.yhp.dao.StudentDao.findbystuid", 1);
System.out.println(student.getSname());
sqlSession2.close();