MyBatis 是一款优秀的 持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
官方文档:http://www.mybatis.org/mybatis-3/zh/index.html
入门
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
XML 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- XML 头部的声明,验证 XML 文档正确性 -->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 类型别名 -->
<typeAliases>
<typeAlias alias="User" type="com.offcn.bean.User"/>
<typeAlias alias="Car" type="com.offcn.bean.Car"/>
</typeAliases>
<!-- mybatis 主环境的配置 -->
<environments default="development">
<environment id="development">
<!-- 事务管理器 -->
<transactionManager type="JDBC" />
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///user" />
<property name="username" value="root" />
<property name="password" value="1234" />
</dataSource>
</environment>
</environments>
<!-- 映射器 -->
<mappers>
<!-- 映射器的 XML 映射文件包含了 SQL 代码和映射定义信息 -->
<mapper resource="com/offcn/mapper/UserMapper.xml" />
<mapper resource="com/offcn/mapper/CarMapper.xml" />
</mappers>
</configuration>
XML 映射文件
对应接口中不同的参数形式,sql语句中参数的写法不同
<?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中如果出现的同名的方法的时候,我们可以使用命名空间区分 -->
<mapper namespace="com.offcn.mapper.UserMapper">
<!-- insert : 插入操作
id: 属性中的方法名称必须和接口中的方法名称保持一致
parameterType: 方法的参数类型 -->
<insert id="saveUser" parameterType="User"><!-- 参数为对象 -->
<!--
selectKey: 执行sql语句获取返回值.并将返回值设置为目标对象的属性
keyProperty: selectKey 语句结果返回应该被设置的目标属性
keyColumn: 匹配属性的返回结果集中的列名称
resultType: 返回类型
order: BEFORE AFTER : sql和返回值获取的先后关系
-->
<selectKey keyProperty="uid" keyColumn="uid" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
<!-- 执行的sql #{传入类型的属性名称}占位符 和jdbc中 ? 一样 -->
insert into user(uid,uname,pwd) values(#{uid},#{uname},#{pwd});
</insert>
<select id="moreParamTest1" resultType="User"><!-- 数组下标 -->
select * from user where uid=#{0} and uname=#{1}
</select>
<select id="moreParamTest2" parameterType="java.util.Map" resultType="User"><!-- 参数为Map -->
select * from user where uid=#{uid} and uname=#{uname}
</select>
<select id="moreParamTest3" resultType="User"><!-- 参数为注解 -->
select * from user where uid=#{uid} and uname=#{uname}
</select>
</mapper>
接口
public interface UserMapper {
//参数为对象
public int saveUser(User user);
//参数为参数列表,对应映射文件中用数组下标
public User moreParamTest1(int uid,String uname);
//参数为Map集合 通过K-V确定
public User moreParamTest2(Map<String,Object> map);
//参数为注解 别名形式
public User moreParamTest3(@Param("uid")int uid,@Param("uname")String uname);
}
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但是也可以使用任意的输入流(InputStream)实例,包括字符串形式的文件路径或者 file:// 的 URL 形式的文件路径来配置。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,可使从 classpath 或其他位置加载资源文件更加容易。
简单应用
package com.offcn.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import com.offcn.bean.Car;
import com.offcn.bean.User;
import com.offcn.mapper.CarMapper;
import com.offcn.mapper.UserMapper;
/**
* @author ZJT
* mybatis
*/
public class TestMybatis {
/**
* 简单入门||一般流程
*/
@Test
public void run() {
// 读取配置文件(是否将mapper.xml加入到主配置中)
try {
// 使用输入流读取外部的mybatis的配置文件
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
// 构建session工厂
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(is);
// 构建session
SqlSession session = sf.openSession();
// 获取接口对象
UserMapper mapper = session.getMapper(UserMapper.class);
// 准备数据
User user = new User();
user.setUname("王五");
user.setPwd("123");
// 接口对象调用方法
mapper.saveUser(user);
// 提交session
session.commit();
// 关闭session
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 多参数处理||数组下标
*/
@Test
public void run1() {
// 读取配置文件(是否将mapper.xml加入到主配置中)
try {
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
SqlSession session = ssf.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.moreParamTest1(7,"王五");
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 多参数处理||Map
*/
@Test
public void run2() {
// 读取配置文件(是否将mapper.xml加入到主配置中)
try {
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
SqlSession session = ssf.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("uid", 7);
map.put("uname", "王五");
User user = mapper.moreParamTest2(map);
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 多参数处理||注解
*/
@Test
public void run3() {
// 读取配置文件(是否将mapper.xml加入到主配置中)
try {
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
SqlSession session = ssf.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.moreParamTest1(7,"王五");
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 插入表时返回一字段封入对象对应属性
*/
@Test
public void run4() {
// 读取配置文件(是否将mapper.xml加入到主配置中)
try {
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
SqlSession session = ssf.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setUname("张三");
user.setPwd("666");
mapper.saveUser(user);
System.out.println(user);
CarMapper carMapp = session.getMapper(CarMapper.class);
Car car = new Car(user.getUid(), "宝马");
carMapp.saveCar(car);
System.out.println(car);
// 提交session
session.commit();
// 关闭session
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ResultMap
基础属性
结果映射.ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系
当类中属性名称和表中字段名称不一致时属性是无法封进去的
<mapper namespace="com.offcn.mapper.UserMapper">
<select id="getUserById" parameterType="User" resultType="User">
select * from user where uid=#{uid}
</select>
</mapper>
resultMap 基本设置
<mapper namespace="com.offcn.mapper.UserMapper">
<!-- 1.解决类中属性名称和表中字段名称不一致
type 类的完全限定名, 或者一个类型别名
id 当前命名空间中的一个唯一标识,用于标识一个结果映射
-->
<resultMap type="User" id="newUser">
<!-- id 和 result 元素都将一个列的值映射到一个简单数据类型的属性或字段
两者之间的唯一不同是,id 元素表示的结果将是对象的标识属性 -->
<id column="uid" property="uid"/><!-- property 映射到列结果的字段或属性 -->
<result column="uname" property="name"/><!-- column 数据库中的列名,或者是列的别名 -->
<result column="pwd" property="pwd"/>
</resultMap>
<select id="getUserById" parameterType="User" resultMap="newUser">
select * from user where uid=#{uid}
</select>
</mapper>
关联元素 || 一对一多表查询
第一种写法
<!-- 2.一对一多表查询 -->
<!-- 基础resultMap,共用的可提取出来 -->
<resultMap type="User" id="newUser">
<id column="uid" property="uid"/>
<result column="uname" property="name"/>
<result column="pwd" property="pwd"/>
<!-- 2.1版本 一对一多表查询.这样写复用性差 -->
<!-- 关联(association)元素处理“有一个”类型的关系.你需要指定目标属性名以及属性的javaType
很多时候 MyBatis 可以自己推断出来.column很多时候可以省略不写 -->
<association column="uid" property="card" javaType="Card">
<id column="uid" property="cid"/>
<result column="cname" property="cname"/>
</association>
</resultMap>
需要将Card封入User作为属性
第二种写法
关联的嵌套结果映射
<resultMap type="User" id="newUser">
<id column="uid" property="uid"/>
<result column="uname" property="name"/>
<result column="pwd" property="pwd"/>
</resultMap>
<!-- 2.2一对一查询 继承扩展 关联的嵌套结果映射
用了外部的结果映射元素来映射关联。这使得结果映射可以被重用
-->
<resultMap type="User" id="newUser1" extends="newUser">
<!-- resultMap 结果映射的 ID-->
<association property="card" javaType="Cadr"
resultMap="com.offcn.mapper.CardMapper.newCard">
</association>
</resultMap>
<select id="getUserAndCard" parameterType="User" resultMap="newUser1">
select u.*,c.* from user u,card c where u.uid=c.uid and u.uid=#{uid}
</select>
其它xml映射文件中被调用的.这样相同写法的resultMap可提取出来,需要的时候通过resultMap=" "调取即可
<?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.offcn.mapper.CardMapper">
<!-- 基础resultMap,共用的可提取出来-->
<resultMap type="Card" id="newCard">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
</resultMap>
</mapper>
第三种写法
查询当前的person对象的时候,我们调用其他的mapper.xml配置文件的中已经写好的select查询(查询出当前对象的时候不想要去立即查询关联对象,只有等到我们想要去使用关联对象的时候在去发送我们sql),将我们的sql进行拆分
<!-- 2.3一对一查询 关联的嵌套 Select 查询
select 用于加载复杂类型属性的映射语句的 ID,
它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句
缺点:在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”
-->
<resultMap type="User" id="newUser2" extends="newUser">
<association column="uid" property="card" javaType="Card"
select="com.offcn.mapper.CardMapper.getCardById"></association>
</resultMap>
<select id="getUserById" parameterType="User" resultMap="newUser2">
select * from user where uid=#{uid}
</select>
<mapper namespace="com.offcn.mapper.CardMapper">
<resultMap type="Card" id="newCard">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
</resultMap>
<!-- 被引用的查询 -->
<select id="getCardById" parameterType="Card" resultMap="newCard">
select * from card where uid=#{uid}
</select>
</mapper>
集合元素 || 一对多多表查询
集合元素和上文关联元素几乎是一样的,它们相似的程度之高,以致于没有必要再介绍集合元素的相似部分。 仅关注它们的不同之处.这里仅介绍一种写法,其它参照关联元素的写法即可.
新加的是集合元素。 里面有新的 “ofType” 属性。这个属性非常重要,它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来.
<!-- ofType="Car" 是一个存储 Car 的集合”. -->
<resultMap type="User" id="newUser5" extends="newUser">
<collection column="uid" property="cars" ofType="Car"
select="com.offcn.mapper.CarMapper.getCarById"></collection>
</resultMap>
<select id="getUserById" parameterType="User" resultMap="newUser5">
select * from user where uid=#{uid}
</select>
被调用的 select id=“getCarById”
<mapper namespace="com.offcn.mapper.CarMapper">
<resultMap type="Car" id="newCar">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
</resultMap>
<select id="getCarById" parameterType="Car" resultMap="newCar">
select * from car where uid=#{uid}
</select>
</mapper>
动态SQL
逆向工程
MyBatis的一个主要的特点就是需要程序员自己编写sql,那么如果表太多的话,难免会很麻烦,所以mybatis官方提供了一个逆向工程,可以针对单表自动生成mybatis执行所需要的代码(包括mapper.xml、mapper.java、po…)。一般在开发中,常用的逆向工程方式是通过数据库的表生成代码
官方文档:http://www.mybatis.org/generator/
运行方式
配置文件\jar包等
1.需要导入的jar包
2.配置文件
主要设置以下信息
连接数据库
指定要生成代码的位置,要生成的代码包括po类,mapper.xml和mapper.java
指定数据库中想要生成哪些表
注意:配置文件最好和src同级放置. targetPackage=""路径指定后会在生成代码后自动创建
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true"></property>
</commentGenerator>
<!-- 设定数据库连接的基本信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/user" userId="root"
password="1234">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL
和 NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO类的位置 -->
<javaModelGenerator targetPackage="com.offcn.bean"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="true" />
<!-- 从数据库返回的值清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- mapper映射文件的位置 -->
<sqlMapGenerator targetPackage="com.offcn.mapper"
targetProject=".\src">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- .xml对应的mapper接口的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.offcn.mapper" targetProject=".\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 设定逆向生成的表 -->
<table tableName="user"></table>
<table tableName="card"></table>
</context>
</generatorConfiguration>
3.基于XML的配置的逆向生成代码
最后运行生成即可