摘要:MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生 Map 使用简单的 XML 或注解,将接口和 Java 的 POJO 映射成数据库中的记录。那么她究竟是什么?
学习大纲如下:
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于 classpath 中即可。
注:其中 x.x.x:代表您所使用的时候的 MyBatis 最新版本。
截止到笔者发稿前,目前的最新版本是:3.4.7-SNAPSHOT。
如果使用 Maven 来构建项目,则需将下面的 dependency 代码置于 pom.xml 文件中:
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>x.x.xversion>
dependency>
注意:其中 x.x.x 代表您所使用的时候的 MyBatis 最新版本。
我们知道:每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
所以我们可以通过下面这个常用的示例了解到 xml 的常用配置:
xml version="1.0" encoding="UTF-8" ?>
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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),这都是我们日常研发中常用的。其中:
type="JDBC"/>
默认情况下,它会关闭连接。然而一些容器并不希望这样,因此如果你需要从连接中停止它,就可以将 closeConnection 属性设置为 false,比如:
"MANAGED">
<property name="closeConnection" value="false"/>
transactionManager>
总而言之,MyBatis 管理事务是分为两种方式:
(大家先整体感知一下,不要在意具体的业务逻辑怎么使用。整体通过下面的代码感受一下 MyBatis 的魅力为先。)
我们接下来:可以看看真实项目的代码示例:(节选)
如果 SSM 整合的话(SpringMVC 4 + MyBatis 3 + Spring 4),可以参考另外一篇 chat:http://gitbook.cn/gitchat/activity/5a618f2b99d4bd32a9872b68
SSM 一起的话,配置交给 Spring,那么我们的实际配置如下:
xml version="1.0" encoding="UTF-8"?>
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
plugin>
plugins>
configuration>
其中 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">
<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>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource">property>
<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>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config/log4j.propertiesvalue>
list>
property>
bean>
beans>
增删改查的 MyBatis 的 xml 版本解析:
"1.0" encoding="UTF-8" ?>
"-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
"com.array.dao.GirlsMapper" >
"BaseResultMap" type="com.array.model.Girls" >
"id" property="id" jdbcType="INTEGER" />
"sname" property="sname" jdbcType="VARCHAR" />
"cometime" property="cometime" jdbcType="VARCHAR" />
"age" property="age" jdbcType="INTEGER" />
"maxscore" property="maxscore" jdbcType="INTEGER" />
"minscore" property="minscore" jdbcType="VARCHAR" />
"Base_Column_List" >
id, sname, cometime, age, maxscore, minscore
"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}
而我们操作的实体类如下:
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();
}
}
我们针对 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 getAll() ;
// 通过id更新
Girls toUpdateByid(Integer sid);
int doUpdateByid(Girls g);
}
也就是我们常用的 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 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 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 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 glist = girlsService.getAll();
List plist = new ArrayList();
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 相对繁琐。
和上面的基本一样,但是我们无需创建繁琐的 mapper 的 xml。
"1.0" encoding="UTF-8" ?>
"-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
"com.array.dao.GirlsMapper" >
"BaseResultMap" type="com.array.model.Girls" >
"id" property="id" jdbcType="INTEGER" />
"sname" property="sname" jdbcType="VARCHAR" />
"cometime" property="cometime" jdbcType="VARCHAR" />
"age" property="age" jdbcType="INTEGER" />
"maxscore" property="maxscore" jdbcType="INTEGER" />
"minscore" property="minscore" jdbcType="VARCHAR" />
"Base_Column_List" >
id, sname, cometime, age, maxscore, minscore
"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}
以上的 xml 如果使用注解的话,是无需创建的。
我们可以通过这个 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 getAll() ;
@Update("update girls set sname=#{pname},age=#{age} where id = #{id}")
Girls toUpdateByid(Integer sid);
int doUpdateByid(Girls g);
}
其他的同 xml 相通即可。
经典案例:老师和班级,一个班主任只属于一个班级,一个班级也只能有一个班主任。
xml 的使用如下:
xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.array.mybatis.onetoone.ClassMapper">
<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 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>
经典案例:一个班级里面对应多个学生,这是一对多;反过来,多个学生对应一个班级,这是多对一。
我们可以创建 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; // 以集合的形式呈现
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 getStu() {
return stu;
}
public void setStu(List stu ) {
this.stu = stu;
}
}
那么我们一对多的 xml 对应关系就如下:
xml version="1.0" encoding="UTF-8" ?>
<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 重点在于
这个是集合,相当于 list 的象征。
通过上面的例子,我们很容易得出,多个学生属于一个班级(跨班此处不考虑)。那么对应的 xml 如下:
xml version="1.0" encoding="UTF-8" ?>
<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 重点在于
这个是集合,相当于一个对象的象征。
MyBatis3.0 添加了 association 和 collection 标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联删除操作。因此在进行实体类多对多映射表设计时,需要专门建立一个关联对象类对相关实体类的关联关系进行描述。下文将以“User”和“Group”两个实体类之间的多对多关联映射为例进行 CRUD 操作。
经典案例:一个用户可以属于多个集体(领导、朋友、同事、亲戚),当然一个集体也包含了多个用户。
这个看似很复杂,但是有了前面的基础,我们就很容易看懂下面的 xml 关系:
对应实体类“Group”,建表语句如下:
CREATE TABLE `group` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(40) collate utf8_unicode_ci default NULL,
PRIMARY KEY (`id`)
)
对应实体类“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`)
)
该类为 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`)
)
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;
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 getGroup() {
return group;
}
public void setGroup(List group ) {
this.group = group;
}
}
代码如下:
import java.util.Date;
import java.util.List;
/**
* @describe: Group实体类
*/
public class Group {
private long id;
private String name;
private List 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 getUser() {
return user;
}
public void setUser(List user) {
this.user = user;
}
}
用于描述 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中去配置:
xml version="1.0" encoding="UTF-8" ?>
<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>
<select id="selectUser" parameterType="long" resultMap="userMap">
select * from user where id = #{id}
select>
<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>
group.map.xml
language-xml version="1.0" encoding="UTF-8"?> ">
<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>
<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>
<if test="id != 0 ">gi.id=#{id}if>
<if test="name != null and name != ''">
or gi.name = #{name}
if>
where>
select>
mapper>
总结:很明显,通过 select 这个查询对应的 map,我们能看清其中多对多的 MyBatis 具体用法。
如果我们要取别名的话可以参考下面或者直接在 xml 中写出对应的真实位置即可,自由选择:
type="com.array.bean.User" alias="User" />
type="com.array.bean.Group"
alias="Group" />
type="com.array.bean.UsertoGroup"
alias="UsertoGroup" />
Hibernate 的优点:
Hibernate 的缺点:
MyBatis的优点:
MyBatis 的缺点: