MyBaties框架简介
Mybatis本来是apache的一个开源项目,原名ibatis,2010年该项目有apache software foundation迁移到google code,并改名为mybatis。
Mybatis是一个优秀的持久层框架,用简单的xml和注解用于配置和原始映射。用接口和java的pojo映射成数据库中的记录。
注:常用的持久层技术
1)jdbc
2)hibernate---ORM
3)mybatis----将sql提取到xml配置文件、动态sql、缓存,原名为Ibatis
4)DBUtils
5)JdbcTemplate
MyBaties框架jar包
mybatis-3.1.1.jar----核心包
asm-3.3.1.jar
cglib-2.2.2.jar
commons-logging-1.1.1.jar
log4j-1.2.16.jar
slf4j-api-1.6.2.jar
slf4j-log4j12-1.6.2.jar
入门Demo
sqlMapConfig.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>
<!-- 为类定义别名
<typeAliases>
<typeAlias type="com.mybatis.domain.User" alias="User"/>
</typeAliases> -->
<environments default="mysqlEnvironment">
<environment id="mysqlEnvironment">
<!-- 事务管理器 -->
<transactionManager type="JDBC" />
<!-- 配置数据源,pooled为mybaties提供的数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///mybatis" />
<!--
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>
-->
</dataSource>
</environment>
</environments>
<!-- 注册mybatis的sql映射文件 -->
<mappers>
<!-- 包装了程序中要执行的sql语句 -->
<mapper resource="com/mybatis/domain/UserMapper.xml" />
</mappers>
</configuration>
UserMapper.xml
<?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">
<!-- namespace没有固定要求,但每一个mapper的namespace必须唯一 -->
<mapper namespace="com.mybatis.domain.User">
<sql id="allColumn">
id,name,address
</sql>
<!-- 当前sql的一个唯一标识,java程序中根据id进行调用
<insert id="" parameterType="">
</insert>
<delete id=""></delete>
<update id=""></update> -->
<select id="selectUserById" parameterType="int" resultType="com.mybatis.domain.User">
select
<include refid="allColumn"/>
from mb_user where id = #{id} <!-- #{id} 固定写法,类似于jdbc中的 ? -->
</select>
</mapper>
关于mybatis的几点说明
1)为了更加直观的看到mybatis执行的sql语句,可以在项目的src目录下加入log4j.properties文件,将程序执行的sql输出到控制台
2)当数据表中的字段和对应的实体类中的属性不完全对应时,可以使用resultMap标签进行字段和属性的对应,这点和hibernate非常类似
<!-- entity - database field mapping -->
<resultMap type="com.mybatis.domain.Book" id="bookMap">
<!-- 描述属性和字段的对应关系 -->
<id column="id" property="id"/> <!-- id 主键 -->
<!-- 普通字段 -->
<result column="name" property="name"/>
<result column="bookPrice" property="price"/>
</resultMap>
3)Sql映射文件中的parameterType和resultType都可以指定为hashmap类型
<!-- hashmap = java.util.HashMap , int = java.lang.Integer -->
<select id="selectBookById4Map" parameterType="int" resultMap="hashmap">
select * from mb_book where id = #{id}
</select>
<insert id="insertBook2Map" parameterType="hashmap">
insert into mb_book values (#{id},#{name},#{bookPrice})
</insert>
4)在sqlMapConfig.xml文件中可以定义别名,来简化开发
<!-- 为类定义别名-->
<typeAliases>
<typeAlias type="com.mybatis.domain.User" alias="User"/>
</typeAliases>
5)利用<sql/>标签提取sql语句中的公共字段
<sql id="allColumn">
id,name,address
</sql>
<select id="selectUserById" parameterType="int" resultType="User">
select
<include refid="allColumn"/>
from mb_user where id = #{id} <!-- #{id} 固定写法,类似于jdbc中的 ? -->
</select>
6)模糊查询
<select id="selectUserByNameLike" parameterType="hashmap" resultType="User">
select <include refid="allColumn"/> from mb_user
where name like '%${name}%'
</select>
<select id="selectUserByNameLike1" parameterType="User" resultType="User">
select <include refid="allColumn"/> from mb_user
where name like '%${name}%'
</select>
注:或为实体类 或 为 map (key为name)
当parameterType为基本类型是 #{p} 大括号中的内容可以随便写,此时其为一个普通占位符,
若parameterType为引用类型时,#{p}大括号中的内容必须与引用类型中的字段相符,并且在应用类型中提供对应的getter和setter方法。
关联查询
配置文件及sql的写法:
客户与订单-一个客户对应多个订单
在客户方描述关系:
private Integer id;
private String name;
private Integer age;
private Set<Order> orders = new HashSet<Order>();
<resultMap type="Customer" id="costomerMap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<!-- field : Set<Order> orders -->
<collection property="orders" ofType="Order">
<!-- user alias name for id in order, if not , c.id is same as o.id,
there will be probloms -->
<id column="oid" property="id"/>
<result column="orderNumber" property="orderNumber"/>
<result column="price" property="price"/>
</collection>
</resultMap>
<select id="selectCustomerOrderById" parameterType="int"
resultMap="costomerMap">
select c.id,c.name,c.age,o.id as
oid,o.orderNumber,o.price,o.customerId
from
itheima_customer c
left join itheima_order o
on c.id = o.customerId
where c.id = #{id}
</select>
从订单方描述关联关系:
private Integer id;
private String orderNumber;
private Double price;
private Customer customer;
<resultMap type="Order" id="orderMap">
<id column="id" property="id"/>
<result column="orderNumber" property="orderNumber"/>
<result column="price" property="price"/>
<association property="customer" javaType="Customer">
<id column="customerId" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</association>
</resultMap>
<select id="selectOrderById" parameterType="int" resultMap="orderMap">
select o.id,o.orderNumber,o.price,o.customerId,c.id as
cid,c.name,c.age
from itheima_order o
left join itheima_customer c
on o.id = c.id
where o.id = #{id}
</select>
动态sql查询与更新
<!-- dynamic select -->
<select id="selectBookByCondition" parameterType="Book" resultMap="bookMap">
select id,name,bookPrice from mb_book where 1 = 1
<if test="id != null">
and id = #{id}
</if>
<if test="name != null">
and name = #{name}
</if>
<if test="price != null">
and bookPrice = #{price}
</if>
</select>
<select id="selectBookByCondition2" parameterType="Book" resultMap="bookMap">
select id,name,bookPrice from mb_book
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="name != null">
and name = #{name}
</if>
<if test="price != null">
and bookPrice = #{price}
</if>
</where>
</select>
<!-- dynamic update -->
<update id="updateBookByCondition" parameterType="Book">
update mb_book
<set>
<if test="name != null">
name = #{name}
</if>
</set>
<where>
<if test="id != null">
id = #{id}
</if>
</where>
</update>
批量插入与批量查询
<!-- batch insert -->
<insert id="batchInsertUsers" parameterType="hashmap">
insert into mb_user (name,address) values
<foreach collection="users" item="var" separator=",">
(#{var.name},#{var.address})
</foreach>
</insert>
时SqlSession session = sessionFactory.openSession();
Map<String,List<User>> users = new HashMap<String,List<User>>();
User user1 = new User("wangwu","changchun");
User user2 = new User("zhaoliu","tianjin");
List<User> user = new ArrayList<User>();
user.add(user1);
user.add(user2);
users.put("users", user);
int result = session.insert("com.mybatis.domain.User.batchInsertUsers", users);
session.commit();
<!-- batch select -->
<select id="batchSelectUsers" parameterType="map" resultMap="userMap">
select u.id, u.name, u.address from mb_user u
<if test="ids.size > 0">
<where>
id in
<foreach collection="ids" item="id" separator="," open="("
close=")">
#{id}
</foreach>
</where>
</if>
</select>
SqlSession session = sessionFactory.openSession();
Map<String,List<Integer>> ids = new HashMap<String,List<Integer>>();
List<Integer> idList = new ArrayList<Integer>();
//idList.add(1);
//idList.add(2);
//idList.add(3);
ids.put("ids", idList);
List<User> users =
session.selectList("com.mybatis.domain.User.batchSelectUsers", ids);
Spring整合myBatis
spring整合mybatis
1)创建一个java项目
2)加入jar包
aopalliance-1.0.jar
commons-logging-1.1.1.jar
mybatis-3.0.3.jar
mybatis-spring-1.0.0-RC3.jar
mysql-connector-java-5.1.5-bin.jar
spring-aop-3.0.5.RELEASE.jar
spring-asm-3.0.5.RELEASE.jar
spring-beans-3.0.5.RELEASE.jar
spring-context-3.0.5.RELEASE.jar
spring-core-3.0.5.RELEASE.jar
spring-expression-3.0.5.RELEASE.jar
spring-jdbc-3.0.5.RELEASE.jar
spring-tx-3.0.5.RELEASE.jar
3)提供mybatis框架的核心配置文件sqlMapConfig.xml
4)提供spring配置文件beans.xml(数据源、SqlSessionFactoryBean、事务管理器、通知、AOP切面)
5)创建数据库和表
6)创建实体类和SQL映射文件
7)提供Dao、service
applicationContext.xml
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd ">
<!-- 数据源 DriverManagerDataSource不是连接池实现-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<!--<property name="username" value="root"/>
<property name="password" value="root"/>
-->
</bean>
<!-- 配置spring整合mybatis的工厂bean -->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:sqlMapConfig.xml"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 通知 -->
<tx:advice id="mybatis_advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- AOP切面 -->
<aop:config>
<aop:pointcut expression="execution(* com.mybatis.service.*.*(..))" id="myBatis_pc"/>
<aop:advisor advice-ref="mybatis_advice" pointcut-ref="myBatis_pc"/>
</aop:config>
<!-- dao -->
<bean id="userDao" class="com.mybatis.dao.impl.UserDaoImpl">
<property name="sqlSessionFactory" ref="sessionFactory"/>
</bean>
<!-- service -->
<bean id="userService" class="com.mybatis.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
sqlMapperConfig.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>
<!-- 为类定义别名-->
<typeAliases>
<typeAlias type="com.mybatis.domain.User" alias="User"/>
</typeAliases>
<!-- 由spring对数据源进行管理 -->
<!--<environments default="mysqlEnvironment">
<environment id="mysqlEnvironment">
事务管理器
<transactionManager type="JDBC" />
配置数据源,pooled为mybaties提供的数据源
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///mybatis" />
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>
</dataSource>
</environment>
</environments>
-->
<!-- 注册mybatis的sql映射文件 -->
<mappers>
<!-- 包装了程序中要执行的sql语句 -->
<mapper resource="com/mybatis/domain/UserMapper.xml" />
</mappers>
</configuration>
*核心:
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
*log4j日志级别
Fatal:致命错误
Error:普通错误
Warn:警告
Info:信息(用的比较多)
Debug:调试信息
Trace:堆栈信息
MyBatis插件(略)
MyBatis一级缓存
框架的内置缓存,不用配置,默认存在,不能卸载,生命周期为session的生命周期。
在同一个session中,先后查询同一个数据,即sql语句完全相同,则数据会从mybatis缓存中取得,而不是去查询数据库。
*MyBatis一级缓存是对对应的引用进行缓存。
测试例子:
@Test
public void firstLevelCacheTest(){
SqlSession session = sessionFactory.openSession();
//发送sql
User user1 =
(User)session.selectOne("com.mybatis.domain.User.selectUserById",1);
//不发送sql
User user2 =
(User)session.selectOne("com.mybatis.domain.User.selectUserById",1);
System.out.println("user1 : " + user1);
System.out.println("user2 : " + user2);
System.out.println(user1 == user2); // true
session = sessionFactory.openSession();
//发送sql
User user3 =
(User)session.selectOne("com.mybatis.domain.User.selectUserById",1);
System.out.println(user3 == user2); //false
}
日志信息:
Created connection 28309992.
11:16:51,326 DEBUG selectUserById:47 - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1aff9e8]
11:16:51,326 DEBUG selectUserById:47 - ==> Preparing: select id,name,address from mb_user where id = ?
11:16:51,407 DEBUG selectUserById:47 - ==> Parameters: 1(Integer)
*两次查询只第一次向数据库发送sql语句。
MyBatis二级缓存
默认没有启用,
在核心配置文件中加入<setting name="cacheEnabled" value="true"/>
在SQL映射文件中加入<cache/>
在SQL语句中使用useCache = "true|false"
测试例子:
sqlMapConfig.xml
<!-- two level cache config. main cache switch -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
UserMapper.xml
<!-- 当前mapper中的sql使用二级缓存 -->
<cache/>
<!-- cache -->
<select id="selectUserById" parameterType="int" resultType="User">
select
<include refid="allColumn"/>
from mb_user where id = #{id} <!-- #{id} 固定写法,类似于jdbc中的 ? -->
</select>
<!-- 该sql不使用二级缓存 -->
<select id="selectUserByIdNoCache" parameterType="int" resultType="User" useCache="false">
select
<include refid="allColumn"/>
from mb_user where id = #{id} <!-- #{id} 固定写法,类似于jdbc中的 ? -->
</select>
SqlSession session = sessionFactory.openSession();
//发送sql,放入一级缓存和二级缓存
User user1 = (User)session.selectOne("com.mybatis.domain.User.selectUserById",1);
//不发送sql,优先从一级缓存中取数据,
User user2 = (User)session.selectOne("com.mybatis.domain.User.selectUserById",1);
session.close();
session = sessionFactory.openSession();
//发送sql,此时从二级缓存中取
User user3 = (User)session.selectOne("com.mybatis.domain.User.selectUserById",1);
//一级缓存是缓存对象的引用,二级缓存是缓存散装的数据,当cache hit时,用这些数据重新组装实体,然后返回
System.out.println(user1 == user3); //false
二级缓存说明
1)当在BeanMapper.xml中配置<cache/>时,默认所有sql都使用二级缓存,如果想将某个sql配置成不使用二级缓存可以在相应的sql标签中配置useCache=false
2)BeanMapper.xml中所有的inert/update/delete语句将默认刷新缓存,这三种sql标签中有
flushCache=”true”默认情况下为true,即同一个sessionFactory当有sql语句更新时缓存自
动刷新,以确保数据的实时性。如果改成false,则不会自动刷新。使用缓存时,如果手
工修改数据库表中的数据则会出现脏读现象,缓存将使用LRU(Leasted Recently used)最
近最少使用策略算法来回收。
<cache eviction=”FIFO” flashIntlevel=”60000” readOnly=”true”>
这个更高级的配置创建了一个FIFO缓存,并每隔60秒刷新,存储结果对象或列表的
512个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改他
们会导致冲突,默认的回收策略是LRU,可用的回收策略有:
(1)LRU:最近最少使用的-移除最长时间不被使用的对象。
(2)FIFO:按对象进入缓存的顺序来移除他们
(3)SOFT:移除基于垃圾回收器状态和软引用规则的对象。
(4)WEAK:若引用-更积极的移除基于垃圾回收器状态和弱引用规则的对象。
应用缓存时存在的问题
应用缓存时,可能会出现脏读现象,即当数据库中的数据被更新后,缓存中的数据没有进行同步,此时缓存中的数据就会与数据库中的数据不一致,当cache hit时,取出来的数据就会与数据库中的数据不一致。
一般情况下,只对不经常变换的且不太重要、出现少量偏差时可以接受的数据进行缓存处理,提供系统响应速度。
MyBatis配置ehcache
*导入jar包
mybatis-ehcache-1.0.2.jar
ehcache-core-2.6.5.jar
slf4j-api-1.6.1.jar
* 提供一个ehcache的配置文件ehcache.xml
* 在SQL映射文件中加入<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 系统临时目录, 或自行制定一个缓存路径 ,当缓存对象个数超过maxElementsInMemory时,会将其缓存到磁盘上-->
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<!--
maxElementsInMemory : 内存中最大缓存对象数,ehcache会按制定的策略去清理内存
eternal : 缓存对象是否永久有效,一旦设置改属性为true,则timeout失效
timeToIdleSeconds : 设置elements在失效前的允许空闲时间,仅当elements不是永久有效时使用。是可选属性,
默认值是0,也就是空闲时间可以是无限大。
timeToLiveSeconds : 设置elements在失效前允许存活时间,最大时间介于创建时间和失效时间之间, 仅当elements
为永久有效时,使用默认值0,也就是elements存活时间无线大
maxElementsOnDisk : 磁盘中最大缓存对象数,0为无限大
diskExpiryThreadIntervalSeconds : 磁盘失效线程运行时间间隔 。
memoryStoreEvictionPolicy : 当maxElementsInMemory限制时,ehcache将根据指定的策略去清理内存,
-->
</ehcache>
BeanMapper.xml
<!-- 将二级缓存设置为ehcache -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>