既然做了SpringMVC的小结,那就顺便做个MyBatis的小结。
MyBatis和Hibernate的执行流程差不多,都是加载配置文件 - 会话工厂建造器 - 会话工厂 - 会话 - 执行具体逻辑。
Configuration->SqlSessionFactoryBuilder/SessionFactoryBuilder->SqlSessionFactory/SessionFactory->SqlSession/Session
且都有懒加载、一二级缓存。
但MyBatis没有Hibernate那么抽象,学习起来相对简单。只需要从其自带的文档MyBatis-User-Guide中搞清楚几个概念即可。配置等更是可以直接从文档中复制。
和Hibernate类似,MyBatis的执行需要配置文件(Cfg文件)和映射文件(一般称为Mapper文件)。
配置文件无需多说,就是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> <properties resource="db.properties" /><!--加载数据库信息--> <typeAliases> <package name="cn.larry.mybatis.po" /><!--类型别名,批量处理--> </typeAliases> <environments default="development"> <environment id="development"> <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> <mappers> <mapper resource="cn/larry/mybatis/mapper/UserMapper.xml" /><!--加载单个映射文件--> </mappers> </configuration>
映射文件,其实看样例是最简单的办法:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="test"> <!-- 需求:根据id查询用户信息 --> <!-- id:唯一标识。
parameterType:参数类型,输入映射的类型。
resultType:返回结果类型,输出映射的类型(总是对应表中一条记录的类型)。
#{}:占位符,如果传递的是基本类型,{}内的名称:任意。
${}:连接符,如果传递的是基本类型,{}内的名称:value。 --> <select id="queryUserById" parameterType="int" resultType="cn.larry.mybatis.po.User"> select * from user where id = #{id} </select> <!-- 根据username查询用户信息 --> <select id="queryUserListByUsername" parameterType="string" resultType="cn.larry.mybatis.po.User"> select * from user where username like '%${value}%' </select> <!-- 根据username查询用户信息 --> <select id="queryUserList" parameterType="string" resultType="cn.larry.mybatis.po.User"> select * from user </select> <!-- 添加用户 --> <insert id="insertUser" parameterType="cn.larry.mybatis.po.User"> <selectKey keyProperty="id" resultType="int" order="AFTER"> select LAST_INSERT_ID() <!-- 插入后返回id,仅限于自增主键;select uuid()则可以返回uuid类型的主键 --> </selectKey> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> <!-- 更改用户 --> <update id="updateUser" parameterType="cn.larry.mybatis.po.User"> update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id} </update> <!-- 删除用户--> <delete id="deleteUserById" parameterType="int"> delete from user where id=#{id} </delete> </mapper>
注意上面的标签名,增删改查的操作都有对应标签(仅仅是为了更好的辨识,本质上仍然是一堆SQL语句)。
另外,有人可能不理解连接符$的含意,其实模糊查询那里已经很明显了。
占位符#是要作为查询的一个参数,独立的参数;连接符$则可以联合已有的数据然后整体作为一个参数。
你可以写成 '%${value}%',但不能写成 '%#{value}%',因为#{value}是个独立的参数,甚至无需引号。
至于标签的属性,看英文即可明白。
MyBatis的执行是直接调用映射文件中的标签id:
User selectOne = sqlSession.selectOne("cn.larry.mybatis.mapper.UserMapper.queryUserById", 1); // 1是queryUserById需要的参数
传统的Dao和DaoImpl是将SqlSessionFactory注入DaoImpl,通过接口方法调用映射文件中的标签id,但这很麻烦,而且冗余代码太多。
@Override public User queryUserById(int id) throws Exception { SqlSession session = sqlSessionFactory.openSession(); User user = session.selectOne("test.queryUserById", id); return user; }
所以Mybatis提供了另一种实现方式:Mapper代理。
这种方式只需要声明接口,再编写映射文件即可。
但是,它要求①接口名和映射文件名一致;②映射文件的namespace就是接口的全名称(无扩展名);③接口方法要与映射文件中的标签id一致。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- mapper代理方式,只需要接口,不需要实现 --> <!-- 1、namespace要和mapper的类全路径名称 --> <mapper namespace="cn.larry.mybatis.mapper.UserMapper"> <select id="queryAllUserList" resultType="user"> SELECT * FROM USER </select> </mapper>
package cn.larry.mybatis.mapper; import java.util.List; import cn.larry.mybatis.po.User; public interface UserMapper { public List<User> queryAllUserList(); }
具体调用:
UserMapper mapper = sqlSession.getMapper(UserMapper.class); // 指定Mapper对应的类(内部是JDK动态代理) List<User> queryAllUserList = mapper.queryAllUserList();
MyBatis提供了resultMap属性,可以将查询的返回结果绑定到指定的属性上。
其他:
一对一:association。javaType。通过主键/id联合。
一对多:collection。ofType。通过主键/id联合。
多对多:由一对一、一对多嵌套联合完成。
注意,标签中使用extends="xxx_id",可以继承已有的mapper。
例如:Orders类中有User类还有OrdersDetail类,而一个mapper是查询订单和用户的数据,另一个mapper是查询Orders、User以及OrdersDetail的数据,那么后者可以继承前者,从而只需要添加对OrdersDetail的查询即可。
延迟加载,就是按需加载,本质上就是将关联的子查询延后加载(未必是外键关联的,同Hibernate)。默认关闭。
前提一:association或collection的内容可以独立出去,再使用select属性引入,column属性是关联的外键。
前提二:settings配置。<setting name="lazyLoadingEnabled" value="true"/> (查找方法:直接查找标签对应的类,查找其中的属性)(所有标签都对应某个类)
上面是标签实现。
mybatis的缓存:
一级缓存,sqlSession级别的缓存,会话级别的缓存。语句和参数都相同。如果执行了commit操作,清空缓存。一级缓存的数据结构hashmap。(数据库也有缓存)
二级缓存,mapper级别的缓存。只针对单表。重点:序列化。
一级缓存始终开启;二级缓存可以设置开启关闭。(cacheEnabled)想使用二级缓存,<cache></cache>即可。见说明。
但,由于一二级缓存都是mybatis的缓存,有局限性,所以通常在分布式架构中使用第三方缓存,如ehcache、redis、memcache等。
ehcache1.7后,为集群提供了五种解决方案,rmi、jms、ehcache server、jgroups、terracotta。rmi不存在主从。
若使用第三方缓存,缓存需要实现mybatis提供的Cache接口。加入jar包,设置配置文件,以及<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>即可。