摘要:MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生 Map 使用简单的 XML 或注解,将接口和 Java 的 POJO 映射成数据库中的记录。那么她究竟是什么?
学习大纲如下:
- MyBatis 概念?Mybatis 的版本管理,Jar 包;
- MyBatis 官方和入门实例基于 XML 配置;
- MyBatis 入门实例注解配置;
- MyBatis 入门实例 一对一;
- MyBatis 入门实例 一对多,多对一;
- MyBatis 入门实例 多对多;
- Mybatis 和 Hibernate 的对比;
- MyBatis 框架的优缺点及其适用场合。
一、MyBatis 概念
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
二、Mybatis 的版本管理和 Jar 包
安装
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于 classpath 中即可。
注:其中 x.x.x:代表您所使用的时候的 MyBatis 最新版本。
截止到笔者发稿前,目前的最新版本是:3.4.7-SNAPSHOT。
如果使用 Maven 来构建项目,则需将下面的 dependency 代码置于 pom.xml 文件中:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
注意:其中 x.x.x 代表您所使用的时候的 MyBatis 最新版本。
三、MyBatis 官方和入门实例基于 XML 配置
我们知道:每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
所以我们可以通过下面这个常用的示例了解到 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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/> <!-- 后面:Array老师详细解析此处 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
很明显可以看出:
包含了数据源(DataSource)的链接信息和事务管理器(TransactionManager),这都是我们日常研发中常用的。其中:
<transactionManager type="JDBC"/>
3.1 事务管理器
- JDBC:这个配置直接简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
- MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期。
默认情况下,它会关闭连接。然而一些容器并不希望这样,因此如果你需要从连接中停止它,就可以将 closeConnection 属性设置为 false,比如:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
总而言之,MyBatis 管理事务是分为两种方式:
- 使用 JDBC 的事务管理机制,就是利用 java.sql.Connection 对象完成对事务的提交
- 使用 MANAGED 的事务管理机制,这种机制 MyBatis 自身不会去实现事务管理,而是让程序的容器(JBOSS、WebLogic)来实现对事务的管理
3.2 代码演示
(大家先整体感知一下,不要在意具体的业务逻辑怎么使用。整体通过下面的代码感受一下 MyBatis 的魅力为先。)
我们接下来:可以看看真实项目的代码示例:(节选)
如果 SSM 整合的话(SpringMVC 4 + MyBatis 3 + Spring 4),可以参考另外一篇 chat:http://gitbook.cn/gitchat/activity/5a618f2b99d4bd32a9872b68
SSM 一起的话,配置交给 Spring,那么我们的实际配置如下:
3.2.1 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>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- 配置分页插件,如果不设计分页,下面的可以不用写 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 配置数据库方言,选定项目所用的数据库 -->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
</configuration>
3.2.2 spring-mybatis.xml
其中 Spring 管理的 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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 1.配置数据源: -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/array"></property>
<property name="username" value="root"></property>
<property name="password" value="1111"></property>
</bean>
<!-- 2.整合MyBatis配置文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 2.1关联数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 2.2扫描MyBatis的在mapper包中的xml 和扫描MyBatis的配置文件-->
<property name="mapperLocations" value="classpath:com/array/mapper/*.xml"></property>
<property name="configLocation" value="classpath:config/mybatis-config.xml"></property>
</bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com/array/dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- 3.事务管理管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4.声明式事务 引用上面定义的事务管理器-->
<tx:annotation-driven transaction-manager="txManager"/>
<!-- 5.加载日志文件等配置文件 -->
<bean id="" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config/log4j.properties</value>
</list>
</property>
</bean>
</beans>
3.2.3 mapper 层
增删改查的 MyBatis 的 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" >
<mapper namespace="com.array.dao.GirlsMapper" >
<resultMap id="BaseResultMap" type="com.array.model.Girls" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="sname" property="sname" jdbcType="VARCHAR" />
<result column="cometime" property="cometime" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
<result column="maxscore" property="maxscore" jdbcType="INTEGER" />
<result column="minscore" property="minscore" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
id, sname, cometime, age, maxscore, minscore
</sql>
<!-- 通过id查询对象-- >
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from girls
where id = #{id,jdbcType=INTEGER}
</select>
<!-- 通过id删除对象-- >
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from girls
where id = #{id,jdbcType=INTEGER}
</delete>
<!-- 通过id插入对象-- >
<insert id="insert" parameterType="com.array.model.Girls" >
insert into girls (id, sname, cometime,
age, maxscore, minscore
)
values (#{id,jdbcType=INTEGER}, #{sname,jdbcType=VARCHAR}, #{cometime,jdbcType=VARCHAR},
#{age,jdbcType=INTEGER}, #{maxscore,jdbcType=INTEGER}, #{minscore,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="com.array.model.Girls" >
insert into girls
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="id != null" >
id,
</if>
<if test="sname != null" >
sname,
</if>
<if test="cometime != null" >
cometime,
</if>
<if test="age != null" >
age,
</if>
<if test="maxscore != null" >
maxscore,
</if>
<if test="minscore != null" >
minscore,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="id != null" >
#{id,jdbcType=INTEGER},
</if>
<if test="sname != null" >
#{sname,jdbcType=VARCHAR},
</if>
<if test="cometime != null" >
#{cometime,jdbcType=VARCHAR},
</if>
<if test="age != null" >
#{age,jdbcType=INTEGER},
</if>
<if test="maxscore != null" >
#{maxscore,jdbcType=INTEGER},
</if>
<if test="minscore != null" >
#{minscore,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<!-- 更新对象-- >
<update id="updateByPrimaryKeySelective" parameterType="com.array.model.Girls" >
update girls
<set >
<if test="sname != null" >
sname = #{sname,jdbcType=VARCHAR},
</if>
<if test="cometime != null" >
cometime = #{cometime,jdbcType=VARCHAR},
</if>
<if test="age != null" >
age = #{age,jdbcType=INTEGER},
</if>
<if test="maxscore != null" >
maxscore = #{maxscore,jdbcType=INTEGER},
</if>
<if test="minscore != null" >
minscore = #{minscore,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.array.model.Girls" >
update girls
set sname = #{sname,jdbcType=VARCHAR},
cometime = #{cometime,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER},
maxscore = #{maxscore,jdbcType=INTEGER},
minscore = #{minscore,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
<!-- 查询秀女列表 -->
<select id="getAll" resultMap="BaseResultMap">
select * from girls
</select>
<select id="toUpdateByid" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from girls
where id = #{id,jdbcType=INTEGER}
</select>
<update id="doUpdateByid" parameterType="com.array.model.Girls" >
update girls
set sname = #{sname,jdbcType=VARCHAR},
cometime = #{cometime,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER},
maxscore = #{maxscore,jdbcType=INTEGER},
minscore = #{minscore,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
3.2.4 bean 层
而我们操作的实体类如下:
package com.array.model;
public class Girls {
private Integer id;
private String sname;
private String cometime;
private Integer age;
private Integer maxscore;
private String minscore;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname == null ? null : sname.trim();
}
public String getCometime() {
return cometime;
}
public void setCometime(String cometime) {
this.cometime = cometime == null ? null : cometime.trim();
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getMaxscore() {
return maxscore;
}
public void setMaxscore(Integer maxscore) {
this.maxscore = maxscore;
}
public String getMinscore() {
return minscore;
}
public void setMinscore(String minscore) {
this.minscore = minscore == null ? null : minscore.trim();
}
}
3.2.5 dao 层
我们针对 xml 进行封装供给 Java 调用的 mapper 类如下:
package com.array.dao;
import java.util.List;
import com.array.model.Girls;
public interface GirlsMapper {
int deleteByPrimaryKey(Integer id);
int insert(Girls record);
//对应xml中的插入
int insertSelective(Girls record);
Girls selectByPrimaryKey(Integer id);
//更新
int updateBy PrimaryKeySelective(Girls record);
int updateByPrimaryKey(Girls record);
// 查询所有
List<Girls> getAll();
// 通过id更新
Girls toUpdateByid(Integer sid);
int doUpdateByid(Girls g);
}
3.2.6 controller 层
也就是我们常用的 rest 接口层:
package com.array.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.array.model.Girls;
import com.array.model.Pie;
import com.array.service.GirlsService;
import com.array.utils.PagedResult;
import com.google.gson.Gson;
/**
* 宫女入册的增删改查控制类
*/
@Controller
public class GirlsController {
@Autowired
private GirlsService girlsService;
/**
* 查询所有姓氏的宫女列表
*/
@RequestMapping(value="getAllOne.do",produces="application/json;charset=utf-8")
@ResponseBody
public ModelAndView getAllOne(HttpServletRequest resquest) {
ModelAndView mv = new ModelAndView();
List<Girls> findAllList = girlsService.getAll();
//resquest.setAttribute("findAllList", findAllList);
mv.addObject("findAllList", findAllList);
mv.setViewName("list");
return mv;
}
/**
* 查询所有姓氏的宫女列表 json
*/
@RequestMapping(value="getAllTwo.do",produces="application/json;charset=utf-8")
@ResponseBody
public String getAllTwo(HttpServletRequest resquest) {
List<Girls> findAllList = girlsService.getAll();
Gson gson = new Gson();
String json = gson.toJson(findAllList);
return json;
}
/**
* 1.线传统分页
* 设置分页的默认值
*/
@RequestMapping(value="getAllByPage.do",produces="application/json;charset=utf-8")
public ModelAndView getAllByPage(@RequestParam(value="pageNumber",defaultValue="1")Integer pageNumber,
@RequestParam(value="pageSize",defaultValue="2")Integer pageSize){
ModelAndView mv = new ModelAndView();
// 当前页和每页的条数
// 传入数据到分页工具类
PagedResult<Girls> pageResult = girlsService.getAllByPage(pageNumber,pageSize);
// 数据传递到前台页面展示层
mv.addObject("pageResult", pageResult);
// 跳转页面
mv.setViewName("listpage");
return mv;
}
/**
* 秀女入宫插入操作
* 使用ajax,所以要用String
*/
@RequestMapping(value="insert.do",produces="application/json;charset=utf-8")
@ResponseBody
public String insert(Girls g) {
int i = girlsService.insert(g);
if(i>0) {
return "yes";
} else {
return "no";
}
}
/**
* 秀女的删除后台操作
* 使用ajax
*/
@RequestMapping(value="del.do",produces="application/json;charset=utf-8")
@ResponseBody
public String del(String id) {
int i = girlsService.del(id);
if(i>0) {
return "yes";
} else {
return "no";
}
}
/**
* 更新操作
* 回显示
*/
@RequestMapping(value="toUpdateByid.do",produces="application/json;charset=utf-8")
@ResponseBody
public ModelAndView toUpdateByid(String id) {
ModelAndView mv = new ModelAndView();
Girls girls = girlsService.toUpdateByid(id);
mv.addObject("girls", girls);
mv.setViewName("toupdate");
return mv;
}
/**
* 更新操作
*/
@RequestMapping(value="doUpdateByid.do",produces="application/json;charset=utf-8")
@ResponseBody
public String doUpdateByid(Girls g) {
int i = girlsService.doUpdateByid(g);
if(i>0) {
return "yes";
} else {
return "no";
}
}
/*
* 玫瑰图
*/
@RequestMapping("getAllByPie.do")
@ResponseBody
public String getAllByPie() {
List<Girls> glist = girlsService.getAll();
List<Pie> plist = new ArrayList<Pie>();
for (Girls girls : glist) {
Pie pie = new Pie();
pie.setValue(girls.getAge().toString());
pie.setName(girls.getSname());
plist.add(pie);
}
Gson gson = new Gson();
String json = gson.toJson(plist);
return json;
}
}
总结:我们通过上面的例子很清楚的看到,最终我们还是去写自己的 SQL 从而达到了 CRUD 的目的。这也是 Hibernate 完全封装所不能办到的。缺点是:xml 相对繁琐。
四、MyBatis 入门实例注解配置
和上面的基本一样,但是我们无需创建繁琐的 mapper 的 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" >
<mapper namespace="com.array.dao.GirlsMapper" >
<resultMap id="BaseResultMap" type="com.array.model.Girls" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="sname" property="sname" jdbcType="VARCHAR" />
<result column="cometime" property="cometime" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
<result column="maxscore" property="maxscore" jdbcType="INTEGER" />
<result column="minscore" property="minscore" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
id, sname, cometime, age, maxscore, minscore
</sql>
<!-- 通过id查询对象-- >
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from girls
where id = #{id,jdbcType=INTEGER}
</select>
<!-- 通过id删除对象-- >
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from girls
where id = #{id,jdbcType=INTEGER}
</delete>
<!-- 通过id插入对象-- >
<insert id="insert" parameterType="com.array.model.Girls" >
insert into girls (id, sname, cometime,
age, maxscore, minscore
)
values (#{id,jdbcType=INTEGER}, #{sname,jdbcType=VARCHAR}, #{cometime,jdbcType=VARCHAR},
#{age,jdbcType=INTEGER}, #{maxscore,jdbcType=INTEGER}, #{minscore,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="com.array.model.Girls" >
insert into girls
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="id != null" >
id,
</if>
<if test="sname != null" >
sname,
</if>
<if test="cometime != null" >
cometime,
</if>
<if test="age != null" >
age,
</if>
<if test="maxscore != null" >
maxscore,
</if>
<if test="minscore != null" >
minscore,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="id != null" >
#{id,jdbcType=INTEGER},
</if>
<if test="sname != null" >
#{sname,jdbcType=VARCHAR},
</if>
<if test="cometime != null" >
#{cometime,jdbcType=VARCHAR},
</if>
<if test="age != null" >
#{age,jdbcType=INTEGER},
</if>
<if test="maxscore != null" >
#{maxscore,jdbcType=INTEGER},
</if>
<if test="minscore != null" >
#{minscore,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<!-- 更新对象-- >
<update id="updateByPrimaryKeySelective" parameterType="com.array.model.Girls" >
update girls
<set >
<if test="sname != null" >
sname = #{sname,jdbcType=VARCHAR},
</if>
<if test="cometime != null" >
cometime = #{cometime,jdbcType=VARCHAR},
</if>
<if test="age != null" >
age = #{age,jdbcType=INTEGER},
</if>
<if test="maxscore != null" >
maxscore = #{maxscore,jdbcType=INTEGER},
</if>
<if test="minscore != null" >
minscore = #{minscore,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.array.model.Girls" >
update girls
set sname = #{sname,jdbcType=VARCHAR},
cometime = #{cometime,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER},
maxscore = #{maxscore,jdbcType=INTEGER},
minscore = #{minscore,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
<!-- 查询秀女列表 -->
<select id="getAll" resultMap="BaseResultMap">
select * from girls
</select>
<select id="toUpdateByid" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from girls
where id = #{id,jdbcType=INTEGER}
</select>
<update id="doUpdateByid" parameterType="com.array.model.Girls" >
update girls
set sname = #{sname,jdbcType=VARCHAR},
cometime = #{cometime,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER},
maxscore = #{maxscore,jdbcType=INTEGER},
minscore = #{minscore,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
以上的 xml 如果使用注解的话,是无需创建的。
4.1 注解的魅力在哪儿?
我们可以通过这个 dao 层入手:看下面每个 CRUD 上面的增加的部分,注解
package com.array.dao;
import java.util.List;
import com.array.model.Girls;
public interface GirlsMapper {
@Delete("delete from vwhere id=#{id}")
int deleteByPrimaryKey(Integer id);
@Insert("insert into girls (id,sname,cometime,age,maxscore,minscore) values(#{id},#{sname},#{cometime},#{age},#{maxscore},#{minscore})")
int insert(Girls record);
int insertSelective(Girls record);
@Select("select * from girls where id= #{id}")
Girls selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(Girls record);
int updateByPrimaryKey(Girls record);
List<Girls> getAll();
@Update("update girls set sname=#{pname},age=#{age} where id = #{id}")
Girls toUpdateByid(Integer sid);
int doUpdateByid(Girls g);
}
其他的同 xml 相通即可。
五、MyBatis 入门实例:一对一
经典案例:老师和班级,一个班主任只属于一个班级,一个班级也只能有一个班主任。
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">
<!-- 为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样保证了namespace的值是唯一的-->
<mapper namespace="com.array.mybatis.onetoone.ClassMapper">
<!-- 嵌套结果:使用封装联表查询的数据(去除重复的数据)
select * from class a, teacher t where a.teacher_id=t.t_id and a.c_id=1
-->
<select id="getClass" parameterType="int" resultMap="getClassMap">
select * from class a, teacher t where a.teacher_id = t.t_id and a.teacher_id=#{id}
</select>
<!-- resultMap:映射实体类和字段之间的一一对应的关系 -->
<resultMap type="Classes" id="getClassMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
</mapper>
六、MyBatis 入门实例:一对多
经典案例:一个班级里面对应多个学生,这是一对多;反过来,多个学生对应一个班级,这是多对一。
我们可以创建 ArrayClass 和 Stu 两个类来说明:
public class Stu {
private int sid;
private String sname;
private ArrayClass classes;
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Classes getClasses() {
return classes;
}
public void setClasses(Classes classes) {
this.classes = classes;
}
}
对应的 ArrayClass .java 如下:
public class ArrayClass {
private int cid;
private String cname;
private List<Stu> stu; // 以集合的形式呈现
public int getCid() {
return cid;
}
public void setCid(int cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public List<Stu> getStu() {
return stu;
}
public void setStu(List<Stu> stu) {
this.stu = stu;
}
}
那么我们一对多的 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">
<mapper namespace="com.array.onetomany.ArrayClassMapper">
<select id="getClass" resultMap="getMap">
select * from class c,stu s where s.cid=c.cid and c.cid=#{cid}
</select>
<resultMap type="com.array.onetomany.ArrayClass" id="getMap">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"/>
<collection property="stu" ofType="com.array.onetomany.Stu">
<id column="sid" property="sid"/>
<result column="sname" property="sname"/>
</collection>
</resultMap>
</mapper>
总结:上面的 xml 重点在于 <collection>
这个是集合,相当于 list 的象征。
七、MyBatis 入门实例:多对一
通过上面的例子,我们很容易得出,多个学生属于一个班级(跨班此处不考虑)。那么对应的 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">
<mapper namespace="com.array.onetomany.StuMapper">
<select id="getStudents" resultMap="getStuMap">
select * from classes c,student s where s.cid=c.cid and s.sid=#{sid}
</select>
<resultMap type="com.array.onetomany.Stu" id="getStuMap">
<id column="sid" property="sid"></id>
<result column="sname" property="sname"/>
<association property="classes" javaType="com.array.onetomany.ArrayClass">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
</association>
</resultMap>
</mapper>
总结:上面的 xml 重点在于 <association>
这个是集合,相当于一个对象的象征。
八、MyBatis 入门实例:多对多
MyBatis3.0 添加了 association 和 collection 标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联删除操作。因此在进行实体类多对多映射表设计时,需要专门建立一个关联对象类对相关实体类的关联关系进行描述。下文将以“User”和“Group”两个实体类之间的多对多关联映射为例进行 CRUD 操作。
经典案例:一个用户可以属于多个集体(领导、朋友、同事、亲戚),当然一个集体也包含了多个用户。
这个看似很复杂,但是有了前面的基础,我们就很容易看懂下面的 xml 关系:
8.1 建立 group 表
对应实体类“Group”,建表语句如下:
CREATE TABLE `group` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(40) collate utf8_unicode_ci default NULL,
PRIMARY KEY (`id`)
)
8.2 建立 user 表
对应实体类“User”,建表语句如下::
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(40) collate utf8_unicode_ci default NULL,
`password` varchar(20) collate utf8_unicode_ci default NULL,
PRIMARY KEY (`id`)
)
8.3 建立 user_group 表
该类为 User 和 Group 两个实体类之间的关系描述对应实体类”UsertoGroup”,建表语句如下:
CREATE TABLE `user_group` (
`user_id` int(11) default NULL,
`group_id` int(11) default NULL,
KEY `FK_user_group_user_id` (`user_id`),
KEY `FK_user_group_group_id` (`group_id`),
CONSTRAINT `FK_user_group_group_id` FOREIGN KEY (`group_id`) REFERENCES `group_info` (`id`),
CONSTRAINT `FK_user_group_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
)
8.4 建立 User 类
Java 代码:
import java.util.Date;
import java.util.List;
/**
* @describe: User实体类 ,讲解多对多
* @author: Array
*/
public class User {
private long id;
private String name;
private String password;
private List<Group> group;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<Group> getGroup() {
return group;
}
public void setGroup(List<Group> group) {
this.group = group;
}
}
8.5 建立实体 Group
代码如下:
import java.util.Date;
import java.util.List;
/**
* @describe: Group实体类
*/
public class Group {
private long id;
private String name;
private List<User> user;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<User> getUser() {
return user;
}
public void setUser(List<User> user) {
this.user = user;
}
}
8.6 建立实体类 UsertoGroup
用于描述 User 和 Group 之间的对应关系,代码如下:
import java.util.Date;
/**
* @describe: 定义User和Group之间的映射关系
*/
public class UsertoGroup{
private User user;
private Group group;
private Date createTime;
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
其中剩下的我们去xml中去配置:
8.7 user 的 xml 代码:user.map.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">
<mapper namespace="com.array.bean.User">
<resultMap type="User" id="userMap">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="password" column="password" />
</resultMap>
<resultMap type="User" id="userGroupMap" extends="userMap">
<collection property="groups" ofType="Group">
<id property="id" column="goupId" />
<result property="name" column="groupName" />
<result property="state" column="state" />
</collection>
</resultMap>
<!-- id查询用户信息 -->
<select id="selectUser" parameterType="long" resultMap="userMap">
select * from user where id = #{id}
</select>
<!-- 根据user表中的id查询用户和组信息 -->
<select id="selectUserGroup" parameterType="long"
resultMap="userGroupMap">
select u.id,u.name,u.password, gi.id as
goupId,gi.name as groupName
from user u left join user_group ug on u.id=ug.user_id
left join group gi on ug.group_id=gi.id where u.id = #{id}
</select>
<!-- 插入-->
<insert id="saveUser" parameterType="User" keyProperty="id"
useGeneratedKeys="true">
insert into user(name,password) values(#{name},#{password})
</insert>
<!-- 保存用户和组之间的关系信息 -->
<insert id="saveRelativity" parameterType="UsertoGroup">
insert into user_group(user_id,group_id)
values(#{user.id},#{group.id})
</insert>
<select id="selM" resultMap="userMap">
select * from user
</select>
</mapper>
8.8 group 的 xml 配置代码:
group.map.xml
language-<?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="com.array.bean.Group">
<resultMap type="Group" id="groupMap">
<id property="id" column="id" />
<result property="name" column="name" />
</resultMap>
<resultMap type="Group" id="groupUserMap" extends="groupMap">
<collection property="users" ofType="User">
<id property="id" column="userId" />
<result property="name" column="userName" />
<result property="password" column="password" />
</collection>
</resultMap>
<!-- 根据Group表中的id -->
<select id="selectGroupUser" parameterType="Group"
resultMap="groupUserMap">
select u.id as userId,u.name as userName,
u.password,
gi.id,gi.name from group gi left
join user_group ug on gi.id=ug.group_id left join user u on
uug.user_id=u.id
<where>
<!--当id为初始值0,不再使用id作为查询条件 -->
<if test="id != 0 ">gi.id=#{id}</if>
<!-- 当name为空或为空串时,不再使用name作为查询条件 -->
<if test="name != null and name != ''">
or gi.name = #{name}
</if>
</where>
</select>
</mapper>
总结:很明显,通过 select 这个查询对应的 map,我们能看清其中多对多的 MyBatis 具体用法。
如果我们要取别名的话可以参考下面或者直接在 xml 中写出对应的真实位置即可,自由选择:
<typeAliases>
<typeAlias type="com.array.bean.User" alias="User" />
<typeAlias type="com.array.bean.Group"
alias="Group" />
<typeAlias type="com.array.bean.UsertoGroup"
alias="UsertoGroup" />
</typeAliases>
九、MyBatis 和 Hibernate 的优缺点对比 & MyBatis 框架的优缺点及其适用场合
Hibernate 的优点:
- Hibernate 是全自动,Hibernate 完全可以通过对象关系模型实现对数据库的操作,拥有完整的 JavaBean 对象与数据库的映射结构来自动生成 SQL。
- 功能强大,数据库无关性好,O/R 映射能力强,需要写的代码很少,开发速度很快。
- 有更好的二级缓存机制,可以使用第三方缓存。
- 数据库移植性良好。
- Hibernate 拥有完整的日志系统,Hibernate 日志系统非常健全,涉及广泛,包括 SQL 记录、关系异常、优化警告、缓存提示、脏数据警告等
Hibernate 的缺点:
- 学习门槛高,精通门槛更高,程序员如何设计 O/R 映射,在性能和对象模型之间如何取得平衡,以及怎样用好 Hibernate 方面需要的经验和能力都很强才行;
- Hibernate 的 SQL 很多都是自动生成的,无法直接维护 SQL;虽然有 HQL 查询,但功能还是不及 SQL 强大,见到报表等变态需求时,HQL 查询要虚,也就是说 HQL 查询是有局限的;Hibernate 虽然也支持原生 SQL 查询,但开发模式上却与 ORM 不同,需要转换思维,因此使用上有些不方便。总之写SQL的灵活度上 Hibernate 不及 MyBatis。
MyBatis的优点:
- 易于上手和掌握,提供了数据库查询的自动对象绑定功能,而且延续了很好的 SQL 使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。
- SQL 写在 xml 里,便于统一管理和优化, 解除 SQL 与程序代码的耦合。
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射。
- 提供对象关系映射标签,支持对象关系组建维护。
- 提供 xml 标签,支持编写动态 SQL,经常使用 MyBatis 的场合。
- 速度相对于 Hibernate 的速度较快。
MyBatis 的缺点:
- 关联表多时,字段多的时候,SQL 工作量很大。
- SQL 依赖于数据库,导致数据库移植性差。
- 由于 xml 里标签id必须唯一,导致 DAO 中方法不支持方法重载。
- 对象关系映射标签和字段映射标签仅仅是对映射关系的描述,具体实现仍然依赖于 SQL。
- DAO 层过于简单,对象组装的工作量较大。
- 不支持级联更新、级联删除。
- MyBatis 的日志除了基本记录功能外,其它功能薄弱很多。
- 编写动态 SQL 时,不方便调试,尤其逻辑复杂时。
- 提供的写动态 SQL 的 xml 标签功能简单~