注:原创作者具有文章的所有权利,转载注明。
MyBetis3.2框架技术
1.MyBatis介绍及基本环境搭建
1.1 MyBatis介绍:
MyBatis 世界上流行最广泛的基于SQL语句的ORM框架,由Clinton Begin 在2002 年创建,其后,捐献给了Apache基金会,成立了iBatis 项目。2010 年5 月,将代码库迁致Google Code,并更名为MyBatis.
1.2 与Hibernate比较
a. 学习成本:MyBatis简单易学(特别是有SQL语法基础的人),较接近JDBC
b. 程序灵活性:MyBatis直接使用SQL,灵活性高
c. 程序执行效律:MyBatis效律高(不用进行实体与数据库表的映射转换)
d. 可移植性:hibernate较好(与数据库关联在配置中完成,HQL语句与数据库无关)
1.3 不适用场合
MyBatis是一个灵活的DAO层解决方案,满足较高的性能要求,可以在很多场合使用,但一般以下场合不建议使用:
a. 需要支持多种数据库或数据库有移植要求
b. 完全动态SQL,例如:字段都要动态生成(框架自动封装,非我们手动拼接)
c. 使用的不是关系数据库(关系型数据库Hibernate和ibatis是都不行的)
1.4 开发步骤(推荐)
1. 新建JAVA项目或WEB项目
部署jar包(包括数据库驱动包):使用MyBatis需要先下载jar包:下载地址http://code.google.com/p/mybatis
2. 编写主配置文件
3. 创建数据库及表(如已创建,可省略)
4. 创建实体类及SQL映射文件
5. 编写数据库接口及实现,编写测试类及测试
1.5 开发示例
1.5.1 新建项目
新建java项目或web 项目。
1.5.2 导入JAR包
导入mybatis、日志包(配置日志配置文件)(7个)和数据库驱动包。
1.5.3 创建myBatis-config.xml
(可以参考用户手册)。
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTDConfig 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--可以设置多个运行环境,满足不同需要,例如开发、测试、生产环境上有不同一配置 -->
<environments default="development">
<environment id="development">
<!--事务管理类型主要有jdbc和managed,前者依赖于数据源获得的连接,后者依赖于容器 -->
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver"value="com.mysql.jdbc.Driver" />
<!-- 如果数据库设置为UTF-8,则URL参数连接需要添加?useUnicode=true&characterEncoding=UTF-8,如下 -->
<property name="url"value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username"value="root" />
<property name="password"value="root" />
</dataSource>
</environment>
</environments>
</configuration>
1.5.4 创建数据库及表
drop databaseif exists mybatis;
create databasemybatisCHARACTERSET UTF8;
use mybatis;
create table dept(
dept_id int primarykey auto_increment,
dept_name varchar(50),
dept_address varchar(50)
);
insert intodept(dept_name,dept_address) values('研发部一部','广州');
insert intodept(dept_name,dept_address) values('研发部二部','广州');
insert intodept(dept_name,dept_address) values('研发部三部','深圳');
select * from dept;
1.5.5 创建实体类:Dept.java
public class Dept implements Serializable {
private Integer deptId;//部门编号
private String deptName;//部门名称
private String deptAddress;//部门地址
public Integer getDeptId() {
return deptId;
}
public voidsetDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public voidsetDeptName(String deptName) {
this.deptName = deptName;
}
public String getDeptAddress() {
return deptAddress;
}
public voidsetDeptAddress(String deptAddress) {
this.deptAddress = deptAddress;
}
@Override
public String toString() {
return "Dept [deptId=" + deptId + ", deptName="+ deptName
+ ", deptAddress=" + deptAddress + "]";
}
}
1.5.6 创建SQL映射文件及修改主配置文件
SQL映射文件:DeptMapper.xml(可以参考用户手册)
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTDMapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 命名空间可以任选命名,但最好要定义一定规则,便于后继的使用 -->
<mapper namespace="cn.itcast.entity.DeptMapper">
<!-- 一般在查询时使用-->
<resultMap type="cn.itcast.entity.Dept"id="deptResultMap">
<id property="deptId"column="dept_id"/>
<result property="deptName"column="dept_name"/>
<result property="deptAddress"column="dept_address"/>
</resultMap>
<!-- 定义插入的sql语句,通过命名空间+id方式被定位 -->
<insert id="insert"parameterType="cn.itcast.entity.Dept">
insert into dept(dept_name,dept_address)values(#{deptName},#{deptAddress});
</insert>
</mapper>
修改myBatis-config.xml,加入映射文件信息
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTDConfig 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
…………
</environments>
<mappers>
<mapper resource="cn/itcast/entity/DeptMapper.xml"/>
</mappers>
</configuration>
1.5.7 编写数据库操作
包括操作接口及实现,接口略,实现类为:DeptDaoImpl.java
public class DeptDaoImpl {
/**
* 用于插入数据到dept表。
* @param dept 部门信息
* @return表示受影响的行数
*/
public intinsert(Dept dept){
/*
* 1.读取配置信息
* 2.构建session工厂
* 3.创建session
* 4.启动事务(可选)
* 5.数据处理
* 6.提交事务、回滚事务(可选)
* 7.关闭session
*/
int i=0;
SqlSession session=null;
String config="myBatis-config.xml";
Reader reader = null;
try {
reader=Resources.getResourceAsReader(config);
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
session=sqlSessionFactory.openSession();
//事务默认自动启动
//SQL映射文件定义的命名空间+SQL语句的ID定位SQL语句,例如下的:cn.itcast.entity.DeptMapper.insert
此处可以只写id,此时寻找sql会先根据id去找,找不到会根据名称空间+sql去找,最好写全名:namespace+id;
i=session.insert("cn.itcast.entity.DeptMapper.insert",dept);
session.commit();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
session.rollback();
}finally{
//关闭reader对象,这里略
session.close();
}
return i;
}
}
1.5.8 编写测试类
需要导入junit包
public class DeptTest {
private staticDeptDaoImpl deptDaoImpl;
@BeforeClass
public staticvoidsetUpBeforeClass() throws Exception {
deptDaoImpl=new DeptDaoImpl();
}
@AfterClass
public staticvoidtearDownAfterClass() throws Exception {
deptDaoImpl=null;
}
@Test
public voidtestInsert() {
Dept dept=new Dept();
dept.setDeptName("市场部");
dept.setDeptAddress("深圳");
int i=deptDaoImpl.insert(dept);
System.out.println("受影响行数:"+i);
}
}
2.基本的CRUD操作
2.1 准备工作(继续使用前面的库表和代码)
2.2 别名与自定义别名
2.2.1 内置别名
对常用的 java 类型,已经内置了一些别名支持。这些别名都是不区分大小写的。(详细参看用户手机)
2.2.2 自定义别名
在myBatis的主配置文件给cn.itcast.entity.Dept类创建别名Dept,后继的DeptMapper.xml配置文件中可以使用别名(不区分大小写)
<!-- 通过别名简化对类的使用 -->
<typeAliases>
<typeAlias type="cn.itcast.entity.Dept"alias="Dept" />
</typeAliases>
2.3 MyBatisUtil线程安全工具类
当系统运行在多线程环境中,当一个线程挂起时,另一个线程就容易获取这个线程或其它线程操作的session,这样当本线程重新恢复运行的状态时,就可能出现session中的被其它线程修改。
可以使用ThreadLocal类来控制本地线程安全,确保线程挂起恢复后获取到的session仍然是自己之前的没被修改的session。
ThreadLocal原理:采用map结构存储用户使用的session对象,key为ThreadLocal.currentThread,value为session对象。
public class MyBatisThreadSecurity {
//ThreadLocal对象中存放SqlSession对象 private static final ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>(); private static SqlSessionFactory sessionFactory; private static String CONFIG_FILE_LOCATION = "MyBatis-config.xml";
static { try { //构建session工厂 buildSessionFactory(); } catch (Exception e) { System.err.println("%%%% Error Creating SessionFactory %%%%"); e.printStackTrace(); } } private MyBatisThreadSecurity() { }
/** * Returns the ThreadLocal Session instance. Lazy initialize * the <code>SessionFactory</code> if needed. * * @return Session * @throws Exception */ public static SqlSession getSession() throws Exception { //获取当钱线程的session SqlSession session = (SqlSession) threadLocal.get();
//当前线程如果没有session,需要重新建立session if (session == null) { //如果sesssion工厂为空,则先创建工厂 if (sessionFactory == null) { buildSessionFactory(); } //避免if中创建session工厂不成功,再次判空 session = (sessionFactory != null) ? sessionFactory.openSession() : null; //创建成功将当前session对象,放入threadLocal中 threadLocal.set(session); }
return session; }
/** * build session factory * */ public static void buildSessionFactory() { Reader reader=null; try { reader=Resources.getResourceAsReader(CONFIG_FILE_LOCATION); sessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (Exception e) { System.err.println("%%%% Error Creating SessionFactory %%%%"); e.printStackTrace(); }finally{ try { //关闭reader reader.close(); } catch (IOException e) { e.printStackTrace(); } } }
/** * Close the single session instance. * */ public static void closeSession(){ //关闭session之前,首先从threadLocal中获取到当前线程的session SqlSession session = (SqlSession) threadLocal.get(); //保证ThreadLocal中没有当前线程的session了。 threadLocal.set(null);
//关闭session if (session != null) { session.close(); } }
/** * return session factory * */ public static SqlSessionFactory getSessionFactory() { return sessionFactory; } }
|
2.3 CRUD操作
2.3.1 新增操作
配置文件DeptMapper.xml使用别名, DeptDaoImpl.java新增方法使用工具类。
修改配置文件DeptMapper.xml(使用别名):
<!--parameterType="Dept"不写时,也能自动根据代码传递的参数Dept自动匹配内容-->
<insert id="insert" parameterType="Dept">
insertinto dept(dept_name) values(#{deptName});
</insert>
修改DeptDaoImpl.java新增方法(使用MyBatisUtil.java工具类):
/**
* @param dept 部门信息
* @return保存信息后受影响的行数
*/
public intsaveDept(Dept dept) {
int i = 0;
try {
session = MyBatisUtil.getSession();
i = session.insert("cn.itcast.entity.DeptMapper.insertDept", dept);
session.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
session.rollback();
} finally {
try {
MyBatisUtil.closeSession();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return i;
}
2.3.2 修改操作
修改配置文件deptMapper.xml,添加
<update id="update"parameterType="Dept">
update dept setdept_name=#{deptName} ,dept_address=#{deptAddress} where dept_id=#{deptId}
</update>
修改DeptDaoImpl.java,添加update方法:
public int update(Dept dept){
SqlSession session = null;
int i=0;
try {
session=MyBatisUtil.getSession();
//方法的第一个参数为DeptMapper.xml文件的命名空间+id
i=session.update("cn.itcast.entity.DeptMapper.update",dept);
System.out.println("受影响行数:"+i);
session.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
session.rollback();
}finally{
MyBatisUtil.closeSession();
}
return i;
}
2.3.3 删除操作
修改配置文件deptMapper.xml,添加
<delete id="delete" parameterType="Dept">
delete from dept where dept_id=#{deptId}
</delete>
修改DeptDaoImpl.java,添加delete方法:
public intdelete(Dept dept){
int i = 0;
try {
session=MyBatisUtil.getSession();
//方法的第一个参数为DeptMapper.xml文件的命名空间+id
i=session.delete("cn.itcast.entity.DeptMapper.delete",dept);
//System.out.println("受影响行数:"+i);
session.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
session.rollback();
}finally{
MyBatisUtil.closeSession();
}
return i;
}
2.3.4 查询操作(返回单条记录)
配置deptMapper.xml文件的resultMap元素及SQL查询语句
<!-- 表字段和实体属性命名一致时可以不配置 -->
<resultMap id="deptResultMap"type="Dept">
<id property="deptId"column="dept_id"/>
<result property="deptName"column="dept_name"/>
<result property="deptAddress"column="dept_address"/>
</resultMap>
<!—省略其它的配置信息 -->
<!—返回单条记录,表字段和对应实体属性命名一致时可以不使用resultMap属性配置,直接使用resultType="返回的全类名或别名",建议使用前者;查询结果为所有字段时,也可以用*表示 -->
<select id="selectOne"parameterType="int" resultMap="deptResultMap" >
select dept_id, dept_name from deptwhere dept_id=#{deptId}
</select>
修改DeptDaoImpl.java,添加selectOne方法:
public Dept selectOne(int deptId){
Dept dept=null;
try {
session=MyBatisUtil.getSession();
dept=(Dept)session.selectOne("cn.itcast.entity.DeptMapper.selectOne",deptId);
System.out.println("dept:"+dept);
} catch (Exception e) {
e.printStackTrace();
}finally{
MyBatisUtil.closeSession();
}
return dept;
}
2.3.5 查询操作(返回多条记录)
修改配置文件deptMapper.xml,添加
<!-- 返回多条记录,返回结果配置的不是集合类型,而是集合元素的类型;参数也可以通过Map等方式封装 -->
<select id="selectList"parameterType="Map" resultMap="deptResultMap">
select * from dept where dept_namelike #{deptName}
</select>
修改DeptDaoImpl.java,添加selectList方法:
public List<Dept> selectList(Mapmap){
List<Dept> depts=null;
try {
session=MyBatisUtil.getSession();
depts=session.selectList("cn.itcast.entity.DeptMapper.selectList",map);
} catch (Exception e) {
e.printStackTrace();
}finally{
MyBatisUtil.closeSession();
}
return depts;
}
测试类代码:
@Test
public voidtestSelectList() {
Map map=new HashMap();
map.put("deptName", "%研%");
List<Dept> depts=deptDaoImpl.selectList(map);
for(Dept dept:depts){
System.out.println("dept:"+dept);
}
}
3.动态SQL操作(手动拼接SQL)
3.1 准备工作
创建表及库,实体类,配置文件(参考上章节内容),以下为建表和库的SQL:
drop databaseif exists mybatis;
create databasemybatisCHARACTERSET UTF8;
use mybatis;
create table dept(
dept_id int primarykey auto_increment,
dept_name varchar(50),
dept_address varchar(50)
);
insert intodept(dept_name,dept_address) values('研发部一部','广州');
insert intodept(dept_name,dept_address) values('研发部二部','广州');
insert intodept(dept_name,dept_address) values('研发部三部','深圳');
select * from dept;
3.2 IF语句
修改配置文件deptMapper.xml,添加
<!-- 动态IF条件 -->
<select id="selectListUseIf"parameterType="Dept" resultMap="deptResultMap">
select * from dept where 1=1
<if test="deptId!=null">
and dept_id=#{deptId}
</if>
<if test="deptName!=null">
and dept_name=#{deptName}
</if>
<if test="deptAddress!=null">
and dept_address=#{deptAddress}
</if>
</select>
修改DeptDaoImpl.java,添加selectListUseIf方法:
//根据参数使用配置文件的IF语句自动填充查询的过滤条件
public List<Dept> selectListUseIf(Dept dept){
List<Dept> depts=null;
try {
session=MyBatisUtil.getSession();
depts=session.selectList("cn.itcast.entity.DeptMapper.selectListUseIf",dept);
} catch (Exception e) {
e.printStackTrace();
}finally{
MyBatisUtil.closeSession();
}
return depts;
}
3.3 WHERE语句
修改配置文件deptMapper.xml,添加
<!-- 动态Where条件 ,一般也需要与if结合使用,与纯if比较,省略了where 1=1-->
<select id="selectListUseWhere"parameterType="Dept" resultMap="deptResultMap">
select * from dept
<where>
<if test="deptId!=null">
and dept_id=#{deptId}
</if>
<if test="deptName!=null">
and dept_name=#{deptName}
</if>
<if test="deptAddress!=null">
and dept_address=#{deptAddress}
</if>
</where>
</select>
注:where标签中的and,不会自动加上。但每个条件都写上and,可以自动根据情况去掉and。会自动加上where关键字
3.4 choose(when,otherwise)语句
模拟if-elseif,修改配置文件deptMapper.xml,添加
<select id="selectListUseChoose"parameterType="Dept" resultMap="deptResultMap">
select * from dept where 1=1
<choose>
<when test="deptId!=null">and dept_id=#{deptId}</when>
<when test="deptName!=null">anddept_name=#{deptName}</when>
<when test="deptAddress!=null">and dept_address=#{deptAddress}</when>
<otherwise>and !1 = 1</otherwise>
</choose>
</select>
注:when标签中的test若为true,则后面的when标签不在执行(所有的when标签只执行最先测试为true的那一个)。所有的when标签测试都为false时,执行otherwise标签。
3.5 SET语句
修改配置文件deptMapper.xml,添加
<!--动态set语句可以用来更新数据 -->
<update id="updateUseSet"parameterType="Dept">
updatedept
<set>
<if test="deptName!=null">dept_name=#{deptName},</if>
<if test="deptAddress!=null">dept_address=#{deptAddress},</if>
</set>
wheredept_id=#{deptId}
</update>
注:更新时使用set标签会自动加上set关键字。标签中的内容不会自动加上“,”,但会根据情况去掉没用的“,”。
3.6 ForEach语句
修改配置文件deptMapper.xml,添加
<!-- 定义根据多个部门ID查询部门相关部门信息的SQL语句 ,resultMap的值是指集合里元素的类型,parameterType不用指定 -->
<select id="selectListUseForeach" resultMap="deptResultMap">
select * from dept where dept_id in
<!-- collection="array或list",array用来对应参数为数组,list对应参数为集合如果是放在map或者类的属性里面可以直接写名字 -->
<foreach collection="array"item="deptId" open="("separator="," close=")">
#{deptId}
</foreach>
</select>
3.7 include语句
修改配置文件deptMapper.xml,添加
<!-- 使用include语句动态插入表的字段及对应的值 -->
<sql id="key">
<!--suffixOverrides=","可以忽略最后“,”号
prefix="(" 在前面加上“(”
suffix=")" 在后面加上“)”
prefixOverrides="," 可以忽略最前面的“,”
-->
<trim suffixOverrides=",">
<if test="deptName!=null">
dept_name,
</if>
<if test="deptAddress!=null">
dept_address,
</if>
</trim>
</sql>
<sql id="value">
<trim suffixOverrides=",">
<if test="deptName!=null">
#{deptName},
</if>
<if test="deptAddress!=null">
#{deptAddress},
</if>
</trim>
</sql>
<insert id="insertUseInclude"parameterType="Dept">
insert into dept(
<include refid="key"/>
) values(
<include refid="value"/>
)
</insert>
4.关联查询
4.1 准备工作
4.1.1 创建项目并导入包
略
4.1.2 配置myBatis-config.xml
与前章节一样,这里略
4.1.3 创建数据库及表:
drop databaseif exists mybatis;
create databasemybatisCHARACTERSET UTF8;
use mybatis;
create table dept(
dept_id int primarykey auto_increment,
dept_name varchar(50),
dept_address varchar(50)
);
create table emp(
emp_id varchar(18) primarykey,
emp_name varchar(50),
emp_sex char(1),
dept_id int
);
insert intodept(dept_name,dept_address) values('研发部一部','广州');
insert intodept(dept_name,dept_address) values('研发部二部','广州');
insert intodept(dept_name,dept_address) values('研发部三部','深圳');
insert intoemp(emp_id,emp_name,emp_sex,dept_id) values('44152199507052110','张大',"男","1");
insert intoemp(emp_id,emp_name,emp_sex,dept_id) values('44152199507052111','张一',"女","1");
insert intoemp(emp_id,emp_name,emp_sex,dept_id) values('44152199507052112','张二',"男","1");
select * from dept;
select * from emp;
4.2 基于association查询(用于多对一或一对一)
4.2.1 创建实体类:Dept.java/Emp.java
部门实体类:Dept.java
public class Dept implements Serializable{
private String deptAddress;
private String deptName;
private Integer deptId;
省略set和get方法
员工实体类:Emp.java
public class Emp implements Serializable{
private String empId;
private String empName;
private String empSex;
private Dept dept;
省略set和get方法
4.2.2 配置DeptMapper.xml/EmpMapper.xml
(重点加入级联的查询语句),并映射文件信息到mybatis-config.xml中:
DeptMapper.xml
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTDMapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.entity.DeptMapper">
<!-- 表字段和对应实体属性命名一致时可以不配置 -->
<resultMap id="deptResultMap"type="Dept">
<id property="deptId"column="dept_id" />
<result property="deptName"column="dept_name" />
<result property="deptAddress"column="dept_address" />
</resultMap>
</mapper>
EmpMapper.xml,配置多对一的关联
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTDMapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.entity.EmpMapper">
<!-- 表字段和对应实体属性命名一致时可以不配置 -->
<resultMap id="empResultMap"type="Emp">
<id property="empId"column="emp_id" />
<result property="empName"column="emp_name" />
<result property="empSex"column="emp_sex" />
<!--association配置对一关联 -->
<association property="dept"column="dept_id" javaType="Dept"resultMap="cn.itcast.entity.DeptMapper.deptResultMap"/>
</resultMap>
<!--根据部门名称查询员工(包括员工所在部门)信息 -->
<select id="selectEmpDeptList"parameterType="Emp" resultMap="empResultMap">
<!-- 访问emp.dept.deptName, 前面不用写emp, 直接写 dept.deptName-->
selecte.*,d.* from emp e inner join dept d on
e.dept_id=d.dept_id where d.dept_name like#{dept.deptName}
</select>
</mapper>
4.2.3 配置文件myBatis-config.xml
<!-- 通过别名简化对类的使用 -->
<typeAliases>
<typeAlias type="cn.itcast.entity.Dept"alias="Dept" />
<typeAlias type="cn.itcast.entity.Emp"alias="Emp" />
</typeAliases>
…….
<!--导入SQL映射文件 -->
<mappers>
<mapper resource="cn/itcast/entity/DeptMapper.xml"/>
<mapper resource="cn/itcast/entity/EmpMapper.xml"/>
</mappers>
4.2.4 编写EmpDaoImpl.java实现查询
public class EmpDaoImpl {
SqlSession session;
public List<Emp> selectEmpDeptList(Emp emp){
List<Emp> emps=null;
try{
session=MyBatisUtil.getSession();
emps=session.selectList("cn.itcast.entity.EmpMapper.selectEmpDeptList",emp);
//session.commit();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
//session.rollback();
}finally{
MyBatisUtil.closeSession();
}
return emps;
}
}
4.2.5 编写测试类
EmplTest.java
@Test
public voidselectEmpDeptList() {
Emp emp=new Emp();
Dept dept=new Dept();
dept.setDeptName("%研%");
emp.setDept(dept);
List<Emp> emps=empDaoImpl.selectEmpDeptList(emp);
for(Emp emp1:emps){
System.out.println("emp="+emp1);
//System.out.println("dept="+emp1.getDept());
}
}
4.3 基于collection查询(用于一对多或多对多)
4.3.1 编写Dept.java/Emp.java实体类
Dept.java
public class Dept implements Serializable{
private String deptAddress;
private String deptName;
private Integer deptId;
private List<Emp> emps;
//省略set和get方法
Emp.java
public class Emp implements Serializable{
private String empId;
private String empName;
private String empSex;
//省略set和get方法
4.3.2 配置DeptMapper.xml
DeptMapper.xml文件,配置resultMap(重点是collection配置)和查询SQL语句:
<mapper namespace="cn.itcast.entity.DeptMapper">
<!-- 表字段和对应实体属性命名一致时可以不配置 -->
<resultMap id="deptResultMap"type="Dept">
<id property="deptId"column="dept_id" />
<result property="deptName"column="dept_name" />
<result property="deptAddress"column="dept_address" />
<!-- collection中resultMap引用的是其它文件的map 需要命名空间+id,例如:cn.itcast.entity.EmpMapper.empResultMap-->
<collection property="emps" ofType="Emp"resultMap="cn.itcast.entity.EmpMapper.empResultMap"/>
</resultMap>
<select id="selectDeptEmpList"parameterType="Dept" resultMap="deptResultMap">
select d.*, e.* from dept d inner joinemp e on d.dept_id=e.dept_idwhere d.dept_name like #{deptName}
</select>
</mapper>
4.3.3 配置EmpMapper.xml文件
不用配置对一关联
……
<mapper namespace="cn.itcast.entity.EmpMapper">
<!-- 表字段和对应实体属性命名一致时可以不配置 -->
<resultMap id="empResultMap"type="Emp">
<id property="empId"column="emp_id" />
<result property="empName"column="emp_name" />
<result property="empSex"column="emp_sex" />
</resultMap>
</mapper>
4.3.4 编写数据库操作类DeptDaoImpl.java
public class DeptDaoImpl {
//同时查询部门及部门员工信息
public List<Dept> selectDeptEmpList(Dept dept){
SqlSession session=null;
List<Dept> deps=null;
try{
session=MyBatisUtil.getSession();
deps=session.selectList("cn.itcast.entity.DeptMapper.selectDeptEmpList",dept);
session.commit();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
session.rollback();
}finally{
MyBatisUtil.closeSession();
}
return deps;
}
}
4.3.5 编写测试类,
@Test
public voidselectDeptEmpList() {
Dept paramDept = new Dept();
paramDept.setDeptName("%研%");
List<Dept> depts = deptDaoImpl.selectDeptEmpList(paramDept);
for (Dept dept : depts) {
System.out.println("dept:" + dept);
}
}
4.4 一对多双向关联查询示例
4.4.1 编写实体类:Dept.java/Emp.java
Dept.java
public class Dept implements Serializable{
private String deptAddress;
private String deptName;
private Integer deptId;
private List<Emp> emps;
//省略set和get方法
Emp.java
public class Emp implements Serializable{
private String empId;
private String empName;
private String empSex;
private Dept dept;
//省略set/get方法
4.4.2 编写DeptMapper.xml/EmpMapper.xml文件
DeptMapper.xml
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTDMapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.entity.DeptMapper">
<!-- 表字段和对应实体属性命名一致时可以不配置 -->
<resultMap id="deptResultMap"type="Dept">
<id property="deptId"column="dept_id" />
<result property="deptName"column="dept_name" />
<result property="deptAddress"column="dept_address" />
</resultMap>
<!-- 一对多时,“多”的关联属性可独立配置resultMap,采用extends继承基本属性的resultMap -->
<resultMap id="deptExtResultMap"type="Dept" extends="deptResultMap">
<!--collection中resultMap引用的是其它文件的map 需要命名空间+id,例如:cn.itcast.entity.EmpMapper.empResultMap-->
<collection property="emps"ofType="Emp"
resultMap="cn.itcast.entity.EmpMapper.empResultMap"/>
</resultMap>
<!--用于部门和员工关联查询,返回部门信息(包含部门员工信息)列表,采用extends继承基本属性的resultMap -->
<select id="selectDeptEmpList"parameterType="Dept" resultMap="deptExtResultMap">
select d.*, e.* from dept d innerjoin emp e on d.dept_id=e.dept_id
where d.dept_name like #{deptName}
</select>
</mapper>
EmpMapper.xml
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTDMapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.entity.EmpMapper">
<!-- 表字段和对应实体属性命名一致时可以不配置 -->
<resultMap id="empResultMap"type="Emp">
<id property="empId"column="emp_id" />
<result property="empName"column="emp_name" />
<result property="empSex"column="emp_sex" />
<!--注意association元素的resultMap的值为没有配置“多”的属性映射的deptResultMap,如下 -->
<association property="dept"column="dept_id" resultMap="cn.itcast.entity.DeptMapper.deptResultMap"/>
</resultMap>
<!-- 用于员工和部门关联查询,返回员工信息(包含部门信息)列表 -->
<select id="selectEmpDeptList"parameterType="Emp" resultMap="empResultMap">
select e.*,d.* from emp e innerjoin dept d on
e.dept_id=d.dept_id where d.dept_name like#{dept.deptName}
</select>
</mapper>
4.4.3 编写数据操作类:DeptDaoImpl.java/EmpDaoImpl.java
DeptDaoImpl.java,查询部门员工信息,返回类型为List<Dept>,关键代码:
public List<Dept> selectDeptEmpList(Deptdept){
SqlSession session=null;
List<Dept>deps=null;
try{
session=MyBatisUtil.getSession();
deps=session.selectList("cn.itcast.entity.DeptMapper.selectDeptEmpList",dept);
session.commit();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
session.rollback();
}finally{
MyBatisUtil.closeSession();
}
return deps;
}
EmpDaoImpl.java
查询员工及其所在部门信息,返回类型为List<Emp >,关键代码
public List<Emp>selectEmpDeptList(Emp emp){
SqlSession session=null;
List<Emp> emps=null;
try{
session=MyBatisUtil.getSession();
emps=session.selectList("cn.itcast.entity.EmpMapper.selectEmpDeptList",emp);
session.commit();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
session.rollback();
}finally{
MyBatisUtil.closeSession();
}
return emps;
}
4.4.4 编写测试类:DeptTest.java/EmpTest.java
DeptTest.java关键代码
//测试部门和员工的关联查询,并遍历装载部门类型的结果集
@Test
public voidselectDeptEmpList(){
Dept paramDept=new Dept();
paramDept.setDeptName("%研%");
List<Dept> depts=deptDaoImpl.selectDeptEmpList(paramDept);
for(Dept dept:depts){
System.out.println("dept:"+dept);
}
}
EmpTest.java关键代码
//测试员工和部门的关联查询,并遍历装载员工类型的结果集
@Test
public voidselectEmpDeptList() {
Emp emp=new Emp();
Dept dept=new Dept();
dept.setDeptName("%研%");
emp.setDept(dept);
List<Emp> emps=empDaoImpl.selectEmpDeptList(emp);
for(Emp emp1:emps){
System.out.println("emp="+emp1);
System.out.println("dept="+emp1.getDept());
}
}
注:sql的编写时把两张表进行连接查询。
5. 缓存
Mybatis和hibernate一样,也使用缓存;缓存分为一级缓存和二级缓存,一级缓存指在SqlSession内;二级缓存能被所有的SqlSession共享。
MyBatis 一级缓存默认是开启的,二级缓存分为全局二级缓存(默认开启),文件级二级缓存,语句级二级缓存(默认开启,在定义sql语句时,标签的属性useCache定义为false时,不用缓存。)。也就是说只要在实体类的配置文件中开启二级缓存就可以使用二级缓存了。
5.1 准备工作
数据库及表信息
drop databaseif exists mybatis;
create databasemybatis CHARACTERSET UTF8;
use mybatis;
create table dept(
dept_id int primarykey auto_increment,
dept_name varchar(50),
dept_address varchar(50)
);
insert intodept(dept_name,dept_address) values('研发部一部','广州');
insert intodept(dept_name,dept_address) values('研发部二部','广州');
insert intodept(dept_name,dept_address) values('研发部三部','深圳');
select * from dept;
实体类:
public class Dept implements Serializable {
private Integer deptId;
private String deptName;
private String deptAddress;
//省略set/get方法
工具类,数据库操作类,测试类(略)
5.2一级缓存测试
关健方法代码:
//用来测试一级缓存
public voidselectDept(Integer deptId){
SqlSession session=null;
try {
session =MyBatisUtil.getSession();
//命名空间+id,如cn.itcast.entity.DeptMapper.selectDept
Dept dept1=session.selectOne("cn.itcast.entity.DeptMapper.selectDept", deptId);
System.out.println("dept1:"+dept1);
Dept dept2=session.selectOne("cn.itcast.entity.DeptMapper.selectDept", deptId);
System.out.println("dept1:"+dept1);
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
MyBatisUtil.closeSession();
} catch (Exception e) {
e.printStackTrace();
}
}
测试用例
@Test //测试一级缓存
public void testCache() {
deptDaoImpl.selectDept(1);
}
测试结果
DEBUG 2014-11-08 16:32:45,484org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSourceforcefully closed/removed all connections.
DEBUG 2014-11-08 16:32:45,578org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2014-11-08 16:32:45,859org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection25374911.
DEBUG 2014-11-08 16:32:45,859org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ooo Using Connection[com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:32:45,859org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select * from dept wheredept_id=?
DEBUG 2014-11-08 16:32:45,906org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Integer)
dept1:Dept [deptId=1, deptName=研发部一部, deptAddress=广州]
dept1:Dept [deptId=1, deptName=研发部一部, deptAddress=广州]
DEBUG 2014-11-08 16:32:45,937org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit totrue on JDBC Connection [com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:32:45,937org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection[com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:32:45,937org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection25374911 to pool.
结果分析:
发现只执行一次SQL语句,第二次查询时直接从缓存中获得,默认开启一级缓存。
5.3 二级缓存(默认配置)测试
操作类关键方法代码
//用来测试二级缓存
public Dept testCache2(IntegerdeptId){
SqlSessionsession=null;
Deptdept=null;
try {
session=MyBatisUtil.getSession();
//命名空间+id,如cn.itcast.entity.DeptMapper.selectDept
dept=session.selectOne("cn.itcast.entity.DeptMapper.selectDept", deptId);
}catch (Exception e) {
e.printStackTrace();
}finally{
try {
MyBatisUtil.closeSession();
}catch (Exception e) {
e.printStackTrace();
}
}
return dept;
}
测试用例代码
@Test //用来测试二级缓存
public void testCache2() {
Deptdept = deptDaoImpl.testCache2(1);
System.out.println("dept:" + dept);
Deptdept1 = deptDaoImpl.testCache2(1);
System.out.println("dept1"+dept1);
}
测试结果
DEBUG 2014-11-08 16:40:52,717org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ooo Using Connection[com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:40:52,717org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select * from dept wheredept_id=?
DEBUG 2014-11-08 16:40:52,764org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Integer)
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit totrue on JDBC Connection [com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection[com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:40:52,811 org.apache.ibatis.datasource.pooled.PooledDataSource:Returned connection 25374911 to pool.
dept:Dept [deptId=1, deptName=研发部一部, deptAddress=广州]
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.datasource.pooled.PooledDataSource: Checked out connection25374911 from pool.
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to falseon JDBC Connection [com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ooo Using Connection[com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select * from dept wheredept_id=?
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Integer)
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit totrue on JDBC Connection [com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:40:52,811org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection[com.mysql.jdbc.JDBC4Connection@18330bf]
DEBUG 2014-11-08 16:40:52,811 org.apache.ibatis.datasource.pooled.PooledDataSource:Returned connection 25374911 to pool.
dept1Dept [deptId=1, deptName=研发部一部, deptAddress=广州]
测试结果分析
发现执行了两次sql语句(红色部分),默认为不启用二级缓存。
5.4二级缓存配置
使用二级缓存机制:需要开启全局缓存,文件级缓存,语句级缓存,才能使用二级缓存。默认情况下文件级缓存没有开启。
5.4.1 全局缓存配置
在mybatis的主配置文件中进行配置:
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTDConfig 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 默认有启用全局缓存的,禁用可以把value设为false,如果这里设为false,Mapper.xml或SQL语句级的缓存配置不再起作用 -->
<setting name="cacheEnabled"value="true"/>
</settings>
<!—省略其它配置信息 -->
</configuration>
5.4.2 Mapper文件级缓存配置
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTDMapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.entity.DeptMapper">
<cache/>
<!—省略其它配置信息-->
</mapper>
5.4.3 SQL语句级缓存配置
在相关的Mapper.xml文件中配置SQL查询,关键代码如下(示例):
<!-- useCache默认值为true,设为false时缓存不起作用 -->
<select id="selectOne"parameterType="int" resultMap="deptResultMap"useCache="true" >
select * from dept wheredept_id=#{id}
</select>
5.4.4缓存分析
修改DeptMapper.xml文件,增加缓存的设置,如下
<!--
<cacheeviction="FIFO" flushInterval="60000" size="512"readOnly="false"/>
创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有以下几种, 默认的是 LRU:
1. LRU – 最近最少使用的:移除最长时间不被使用的对象。
2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3. SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
4. WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval:刷新间隔时间,可以被设置为任意的正整数,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size:内存资源数目,可以被设置为任意正整数。默认值是1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
-->
<cache eviction="LRU" size="2"readOnly="false" />
测试代码
@Test
public voidtestSelectDept() {
deptDaoImpl.selectDept(1);
deptDaoImpl.selectDept(2);
deptDaoImpl.selectDept(2);
deptDaoImpl.selectDept(1);
deptDaoImpl.selectDept(3);
deptDaoImpl.selectDept(1);
deptDaoImpl.selectDept(2);
}
6.XML 中的特殊字符处理
如果 MyBatis 使用 XML 配置,那不可避免地会遇到一些对 XML 来说是特殊的字符。如小于号 “<”,因此要进行转义。主要有两个方式:
6.1 使用转义实体
下面是五个在 XML 文档中预定义好的转义实体:
< < 小于号
> > 大于号
& &
' ' 单引号
" " 双引号
小于等于“<=”,其转义为:<=
大小等于“>=”,转义为:>=
6.2 使用 CDATA 部件
CDATA 部件以"<![CDATA["标记开始,以"]]>"标记结束。在"<![CDATA["和"]]>"之间 的特殊字符的意义都不起作用,而转变为普通字符串内容。
在 MyBatis 的 XML 映射语句配置文件中,如果 SQL 语句有特殊字符,使用CDTA 部件括起来,如:
<select id="selectBlog_use_collection"
resultMap= "blogResult" >
<![CDATA[ SELECT id , title, author_idas authored FROM BLOG WHERE ID > 0 and ID < 10 ]]> </select>
7常用批量操作
7.1准备数据
drop databaseif exists mybatis;
create databasemybatisCHARACTERSET UTF8;
use mybatis;
create table dept(
dept_id int primarykey auto_increment,
dept_name varchar(50),
dept_address varchar(50)
);
insert intodept(dept_name,dept_address) values('研发部一部','广州');
insert intodept(dept_name,dept_address) values('研发部二部','广州');
insert intodept(dept_name,dept_address) values('研发部三部','深圳');
--insert into dept( dept_name, dept_address ) values ('研发部4部','广州'),('研发部5部','广州'),('研发部6部','广州')
select * from dept;
7.2批量新增员工
7.2.1映射文件定义SQL
<sql id="key">
<!--suffixOverrides=","可以忽略最后“,”号 -->
<trim suffixOverrides=",">
dept_name,
dept_address,
</trim>
</sql>
<insert id="insertDeptList">
insert into dept(
<include refid="key"/>
) values
<foreach collection="list"item="item" separator=",">
(#{item.deptName},#{item.deptAddress})
</foreach>
</insert>
7.2.2编写批量添加部门方法
数据操作类定义批量添加部门的方法
public int saveDeptList(List<Dept> depts) {
int i = 0;
SqlSession session = null;
try {
session = MyBatisUtil.getSession();
i = session.insert("cn.itcast.entity.DeptMapper.insertDeptList", depts);
session.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
session.rollback();
} finally {
MyBatisUtil.closeSession();
}
return i;
}
7.2.3编写测试代码
@Test
public voidtestInsertDeptList() {
List<Dept> depts = newArrayList<Dept>();
for(int i=0;i<5;i++){
Dept dept = new Dept();
dept.setDeptName("deptname"+i);
dept.setDeptAddress("address"+i);
depts.add(dept);
}
int i=deptDao.saveDeptList(depts);
System.out.println("受影响的行数:"+i);
}
7.3批量删除员工
7.3.1映射文件定义SQL
<delete id="deleteDeptList">
delete from dept where dept_id in
<foreach collection="list"item="item" open="("close=")"
separator=",">
#{item}
</foreach>
</delete>
7.3.2编写批量删除部门的方法
public int deleteDeptList(List<Integer> deptIds){
int i = 0;
SqlSession session = null;
try {
session = MyBatisUtil.getSession();
i = session.delete("cn.itcast.entity.DeptMapper.deleteDeptList", deptIds);
session.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
session.rollback();
} finally {
MyBatisUtil.closeSession();
}
return i;
}
7.3.3编写测试代码
@Test
public voidtestDeleteDeptList() {
List<Integer> deptIds = newArrayList<Integer>();
for(int i=4;i<7;i++){
deptIds.add(i);
}
int i=deptDao.deleteDeptList(deptIds);
System.out.println("受影响的行数:"+i);
}
7.4批量修改员工信息
7.4.1修改mybatis-config.xml文件
支持上有点麻烦,需要修改mybatis-config.xml文件相关数据库连接的信息(主要红色部分),以支持批量更新
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true" />
7.4.2配置批量更新的sql
<update id="updateDeptList">
<!-- 多个update语句,用;号分隔开,如果是oracle数据库,一般需要加“begin”前缀,后缀“end;" -->
<foreach collection="list"item="dept" separator=";">
update dept
<set>
<if test="dept.deptName!=null">dept_name=#{dept.deptName},</if>
<if test="dept.deptAddress!=null">dept_address=#{dept.deptAddress},</if>
</set>
where dept_id=#{dept.deptId}
</foreach>
</update>
7.4.3编写批量更新部门的方法
public int updateDeptList(List<Dept> depts) {
int i = 0;
SqlSession session = null;
try {
session = MyBatisUtil.getSession();
i = session.update("cn.itcast.entity.DeptMapper.updateDeptList", depts);
session.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
session.rollback();
} finally {
MyBatisUtil.closeSession();
}
return i;
}
7.4.4编写测试代码
@Test
public voidtestUpdateDeptList() {
List<Dept> depts = newArrayList<Dept>();
for(int i=1;i<4;i++){
Dept dept = new Dept();
dept.setDeptId(i);
dept.setDeptName("deptName"+i);
dept.setDeptAddress("deptAddress"+ i);
depts.add(dept);
}
int i=deptDao.updateDeptList(depts);
System.out.println("受影响的行数:"+i);
}
8. Spring+myBatis整合
8.1 准备工作
新建项目并导入jar包
配置mybatis-config.xml
创建库及表
创建实体
编写映射文件,修改mybatis-config.xml内容
进行简单测试(除了导入spring相关jar包外,以上内容可能直接使用上一章节内容)
8.2 配置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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-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/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 配置数据源,记得去掉myBatis-config.xml的数据源相关配置 -->
<bean id="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass"value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl"value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8"/>
<property name="user"value="root" />
<property name="password"value="root" />
</bean>
<!-- 配置session工厂 -->
<bean id="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource"ref="dataSource" />
<property name="configLocation"value="classpath:myBatis-config.xml" />
</bean>
<!-- 配置事务管理器,管理数据源事务处理
<beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<propertyname="dataSource" ref="dataSource" />
</bean>-->
<!-- 配置事务管理器,管理数据源事务处理-->
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"ref="dataSource" />
</bean>
<!-- 配置事务通知 -->
<tx:advice id="advice"transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*"propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*"propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*"propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*"propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!-- 配置切面织入的范围 -->
<aop:config>
<aop:advisor advice-ref="advice"pointcut="execution(* cn.itcast.dao.impl.*.*(..))"/>
</aop:config>
<!-- 配置SessionTemplate,已封装了繁琐的数据操作-->
<bean id="sqlSessionTemplate"class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory"ref="sqlSessionFactory"/>
</bean>
<!-- DAO层部门信息表的数据操作对象
<bean id="deptDao"class="cn.itcast.dao.impl.DeptDaoImpl" >
<property name="sqlSessionTemplate"ref="sqlSessionTemplate"/>
</bean>
<bean id="deptService"class="cn.itcast.service.impl.DeptServiceImpl">
<property name="deptDao"ref="deptDao"/>
</bean>
-->
</beans>
8.3 修改myBatis-config.xml
去掉数据源配置
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTDConfig 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="cn.itcast.entity.Dept"alias="Dept" />
</typeAliases>
<mappers>
<mapper resource="cn/itcast/entity/DeptMapper.xml"/>
</mappers>
</configuration>
8.4 编写dao层接口及实现
DeptDao.java
public interface DeptDao {
//根据部门ID查询部门信息
public Dept selectOne(int deptId);
}
修改接口实现类:DeptDaoImpl.java
public class DeptDaoImpl implements DeptDao{
private SqlSessionTemplate sqlSessionTemplate;
public SqlSessionTemplate getSqlSessionTemplate() {
return sqlSessionTemplate;
}
public voidsetSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
//根据部门ID查询部门信息
public Dept selectOne(int deptId){
System.out.println("dao :"+deptId);
//SqlSessionsession=null;
Dept dept=null;
try {
dept=sqlSessionTemplate.selectOne("cn.itcast.entity.DeptMapper.selectOne",deptId);
System.out.println("dao.dept:"+dept);
} catch (Exception e) {
e.printStackTrace();
}
return dept;
}
}
8.5 编写业务层代码
业务层接口略,这里只写业务层实现类:DeptServiceImpl.java
public class DeptServiceImpl {
private DeptDao deptDao;
public Dept selectOne(int deptId){
Dept dept=deptDao.selectOne(deptId);
return dept;
}
public DeptDao getDeptDao() {
return deptDao;
}
public voidsetDeptDao(DeptDao deptDao) {
this.deptDao = deptDao;
}
}
8.6 配置bean信息到sping配置文件
<!-- DAO层部门信息表的数据操作对象 -->
<bean id="deptDao"class="cn.itcast.dao.impl.DeptDaoImpl" >
<property name="sqlSessionTemplate"ref="sqlSessionTemplate"/>
</bean>
<!-- 业务层部门信息业务处理对象 -->
<bean id="deptService"class="cn.itcast.service.impl.DeptServiceImpl">
<property name="deptDao"ref="deptDao"/>
</bean>
8.7 编写测试类
@Test
public voidselectOne() {
ApplicationContext context=newClassPathXmlApplicationContext("applicationContext.xml");
DeptServiceImpldeptService=(DeptServiceImpl)context.getBean("deptService");
Dept dept = deptService.selectOne(1);
System.out.println("dept:" + dept);
}
8.8 简化配置
8.8.1 扫描式加载SQL映射文件
修改myBatis-config.xml文件,去掉<mappers>配置
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTDConfig 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="cn.itcast.entity.Dept"alias="Dept" />
</typeAliases>
<!-- 采用扫描式加载映射文件,以下将不用配置,可以减少映射文件过多时维护的麻烦-->
<!-- <mappers>
<mapper resource="cn/itcast/entity/DeptMapper.xml"/>
</mappers>
-->
</configuration>
修改applicationContext.xml,为SqlSessionFactoryBean设置mapperLocations属性
<!-- 配置session工厂 -->
<bean id="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource"ref="dataSource" />
<property name="configLocation"value="classpath:myBatis-config.xml" />
<!-- 配置扫描式加载SQL映射文件 -->
<property name="mapperLocations"value="classpath:cn/itcast/entity/*.xml"/>
</bean>
8.8.2 MapperScannerConfigurer简化配置
1) 在spring配置文件中添加MapperScannerConfigurer 配置并去掉所有的Dao接口实现类配置
<!-- 配置转换器,对于在basePackage设置的包(包括子包)下的接口类的全类名和在Mapper.xml文件中定义过的命名空间一致,
spring将会生成对应的代理对象(在调用的地方通过@Autowired方式将可以注入接口实例)-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactory"ref="sqlSessionFactory"/>
<property name="basePackage"value="cn.itcast.dao"/>
</bean>
<!-- DAO层部门信息表的数据操作对象,上面如果配置MapperScannerConfigurer转换器,DAO接口将不再使用实现类 -->
<!--
<bean id="deptDao"class="cn.itcast.dao.impl.DeptDaoImpl" >
<propertyname="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
-->
<!-- 业务层部门信息业务处理对象 -->
<bean id="deptService"class="cn.itcast.service.impl.DeptServiceImpl">
<!-- 上面如果配置MapperScannerConfigurer转换器,DAO接口将不再使用实现类注入 -->
<!-- <propertyname="deptDao" ref="deptDao"/> -->
</bean>
2) 检查或修改DeptMapper.xml文件:
注意:命名空间+id和接口+方法名 一致
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTDMapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 这时的命名空间就需要和dao接口类全类名一致了 -->
<mapper namespace="cn.itcast.dao.DeptDao">
<!-- 表字段和对应实体属性命名一致时可以不配置 -->
<resultMap id="deptResultMap"type="Dept">
<id property="deptId"column="dept_id" />
<result property="deptName"column="dept_name" />
<result property="deptAddress"column="dept_address" />
</resultMap>
<!-- 这时的id就需要和dao接口的方法一致了 -->
<select id="selectOne"parameterType="int" resultMap="deptResultMap">
select * from dept wheredept_id=#{id}
</select>
</mapper>
3)业务类中,使用@Autowired为DAO接口注入对象
public class DeptServiceImpl {
@Autowired
private DeptDao deptDao;
//省略其它代码
4)删除Dao实现类(存在也没有意义)
学生动手练习:
完成spring与mybatis整合