Mybatis(三)

152 阅读6分钟

ThreadLocal

  • ThreadLocal:线程容器,可以给线程绑定一个Object内容。 只要线程不变,任何时刻,线程都绑着一个Object对象,可以往里面放东西。一旦改变线程,就无法取出。
  • 匿名内部类在引用参数时,要么是final,要么就是全局的,怕被改变对象地址。
public static void main(String[] args) {
	final ThreadLocal<String> tl = new ThreadLocal<String>();
	tl.set("测试");
		
	new Thread() {
		public void run() {
			String result = tl.get();
			System.out.println(result);//结果为null
		}
	}.start();
}

OpenSessionView

最早是由Spring框架提出的,整合Hibernate框架使用。 原理是利用ThreadLocal能绑定对象,且从servlet到service到Mybatis都是一个线程。编写Mybatis工具类,简化service代码。

  • fifter过滤器

    • 使用注解配置@WebFilter("/*")
    • 在web.xml中配置
<filter>
	<filter-name>opensession</filter-name>
	<filter-class>filter.OpenSessionInView</filter-class>
	</filter>
	<filter-mapping>
	<filter-name>opensession</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping> 

在过滤器中设置session

@Override
    public void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain)
	throws IOException, ServletException {
	try {
		filterchain.doFilter(servletrequest, servletresponse);
		session.commit();
	} catch (Exception e) {
		session.rollback();
		e.printStackTrace();
	}finally{
		MyBatisUtil.closeSession();
	}
}
  • MybatisUtil.java

public class MyBatisUtil {
	//factory实例化的过程是一个比较耗费性能的过程.
	//保证有且只有一个factory
	private static SqlSessionFactory factory;
	private static ThreadLocal<SqlSession> tl = new ThreadLocal<>();
	static{
	    try {
		InputStream is = Resources.getResourceAsStream("mybatis.xml");
		factory = new SqlSessionFactoryBuilder().build(is);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    }
	}
	/**
	 * 获取SqlSession的方法
	 */
	public static SqlSession getSession(){
	    SqlSession session = tl.get();
	    if(session==null){
		tl.set(factory.openSession());
     	    }
	    return tl.get();
	}
	
	public static void closeSession(){
	    SqlSession session = tl.get();
		if(session!=null){
		    session.close();
		}
		tl.set(null);
	}
}

如此一来,service中的代码大量减少 例如在Log类中插入的service

@Override
public int ins(Log log) {
	SqlSession session = MyBatisUtil.getSession();
	LogMapper mapper = session.getMapper(LogMapper.class);
	return mapper.ins(log);
}

缓存

  • 缓存的意义:应用程序和数据库交互的过程是一个相对比较耗时的过程,缓存可以减少应用程序对数据库的访问,提升系统效率。
  • 在Mybatis默认Sqlsession缓存开。
    (1) 即同一个SQLSession对象调用同一个select时,只有第一次访问数据库,第一次之后把查询结果缓存到SQLSession中。
    (2)缓存的是statement对象。在Mybatis中,一个select对象一个statement对象。
  • 缓存流程
    • 先去缓存区查看是否有statement,返回结果
    • 如果没有缓存statement对象,去数据库获取数据
    • 数据库返回查询结果
    • 把查询结果放到对应的缓存区中
  • SQLSessionFactory缓存(二级缓存):当数据被频繁使用,很少被修改
    有效范围:同一个factory中,哪一个SQLSession都可以获取
    • 使用步骤: 在mapper.xml中添加,如果不写readOnly属性,需要把实体类序列化
      <cache readOnly="true"></cache>
      当SQLSession对象关闭或提交时,会把SQLSession缓存对象刷(flush)到SQLSessionFactory缓存区中。

多表查询

  1. 实现方式

  • 业务装配:对两个表编写表单查询语句,在业务(Service)把查询结果进行关联。
  • Auto Mapping:利用该特性,在实现两表联合查询时通过别名完成映射。
  • resultMap:使用Mybatis的resultMap标签
  1. 多表查询时,类中包含另一个类的对象的分类

  • 单个对象
  • 集合对象
  1. resultMap标签

  • 写在mapper.xml中,由程序员控制SQL查询结果与实体类的映射关系,默认使用Auto Mapping特性。
  • 使用resultMap标签时,select标签是使用resultMap 而不使用resultType属性。

例如:搜索People类,其中实体类中的id属性为id1,name属性为name1

<select id="selAll" resultMap="mymap">
    select * from people 
</select>
<resultMap type="people" id="mymap">
    <!-- 主键使用id标签配置映射关系 -->
    <!-- 
    	column:映射到数据库中的属性名
    	property:实体类中的属性名
    -->
    <id column="id" property="id1"/>
    <!-- 其他列使用result标签配置映射关系 -->
    <result column="name" property="name1"/>
</resultMap>
  1. resultMap实现关联单个对象(N+1)

  • N+1查询方式:先查询出某个表的全部信息,再根据这个表的信息查询另一个表的信息。
  • 与业务装配的区别:以前在service写的代码,有Mybatis完成装配
    例:student类中包含teacher对象,查询学生对应的老师
    StudentMapper
<resultMap type="student" id="stuMap">
	<result column="tid" property="tid"/>
	<!-- 如果关联一个对象 -->
	<association property="teacher" select="com.bjsxt.mapper.TeacherMapper.selById" column="tid"></association>
</resultMap>
<select id="selAll" resultMap="stuMap">
	select * from student
</select>
<select id="selAll1" resultMap="stuMap1">
	select * from student 
</select>

TeacherMapper

<select id="selById" resultType="teacher" parameterType="int">
	select * from teacher where id=#{0}
</select>

其中:
association:装配一个对象时使用该标签
property:对象在类中的属性名
select:通过哪个查询查询出该对象的信息
column:把当前表的哪一列的值作为参数传递给另一个查询

  • 注意:使用N+1方式时,列名和属性名一致可以不配置,使用AutoMapping特性。但是Mybatis默认只会给列配置一次。如上例中,tid作为值传递给第二个查询,则不会给它配置id属性,需要手动配置。
  1. resultMap加载集合对象

N+1

使用collection标签
例如:查询老师的所有学生,一个老师对应多个学生 TeacherMapper

<resultMap type="teacher" id="mymap">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <collection property="list" select="com.bjsxt.mapper.StudentMapper.selByTid" column="id">
    </collection>
</resultMap>
<select id="selAll" resultMap="mymap">
    select * from teacher
</select>

StudentMapper

<select id="selByTid" parameterType="int" resultType="student">
    select * from student where tid=#{0}
</select>

多表联合查询方式

Mybatis可以通过主键判断对象是否被加载过,所以不会重复创建Teacher

<resultMap type="teacher" id="mymap1">
    <id column="tid" property="id"/>
    <result column="tname" property="name"/>
    <collection property="list" ofType="student" >
    	<id column="sid" property="id"/>
    	<result column="sname" property="name"/>
    	<result column="age" property="age"/>
    		<result column="tid" property="tid"/>
    </collection>
</resultMap>
<select id="selAll1" resultMap="mymap1">
    select t.id tid,t.name tname,s.id sid,s.name sname,age,tid from teacher t LEFT JOIN student s on t.id=s.tid;
</select>
  1. AutoMapping结合别名实现多表查询

这种方式不能使用N+1,只能使用多表联合。
要求:查询出的列名和属性名一致。
注意:.在SQL是关键字,需要在用反单引号包住别名。

<select id="selAll" resultType="student">
    select t.id `teacher.id`,t.name `teacher.name`,s.id id,s.name name,age,tid 
from student s LEFT JOIN teacher t on t.id=s.tid
</select>

注解

  • 目的:简化mapper.xml文件。
  • 原因:如果涉及动态SQL,仍然使用mapper.xml。
  • 注意:
    (1)mapper.xml与注解可以共存 (2)使用注解时,mapper.xml中的mappers标签中要么使用package,或者class标签。
    <mapper class="mapper.XXXMapper">
  • 使用:直接在接口方法上写
    格式为:@Select/Insert/Update/Delete("SQL语句")
@Select("select * from student")
List<Student> selAll();
  • 若要使用ResultMap则比较麻烦,需要在SQL语句上方再加一句注解。
    例:N+1查询集合对象
@Resuls(value={  
    @Result(id=true,property="id" column="id"),
    @Result(porperty="name" column="name"),
    @Result(porterty="list" column="id",many=@Many(namespace+方法名)
})

运行原理

运行过程中涉及的类

  • Resource:Mybatis中IO流的工具类,用于加载配置文件
  • SQLSessionFactoryBuilder():构造器,创建SQLSession接口的实现类
  • XMLConfigBuilder:Mybatis全局配置文件构造器类,负责读取流内容并转换为Java代码。
  • Configuration:封存了全局配置文件内容
  • DefaultSessionFactory:是SQLSessionFactory的实现类
  • Transaction事务类:每一个SQLSession会带有一个Transaction对象
  • TransactionFactory:事务工厂,负责生产Transaction
  • Executor:Mybatis执行器,负责执行SQL命令,相当于JDBC的statement对象,默认的执行器SimpleExecutor,批量操作是BatchExecutor
  • DefaultSqlSession:SqlSession的实现类
  • ExeceptionFactory:Mybatis异常工厂