@qdlxsxj
2016-05-29T14:19:50.000000Z
字数 38020
阅读 1004
MyBatis
- MyBaits简介
- HelloWorld实现
- 配置文件讲解
- enviromenets
- transactionManager
- dataSource
- properties
- typeAliases
- mappers
- XML配置SQL映射器
- INSERT映射语句
- UPDATE映射语句
- DELETE映射语句
- SELECT映射语句
- MyBatis关系映射
- 一对一关系
- 一对多关系
- Mybatis动态SQL
- if条件
- choose,when,otherwrise
- where
- trim
- foreach
- set
- MyBatis其他
- CLOB类型处理
- BLOB类型处理
- MyBatis分页
- MyBatis缓存
- 注解配置SQL映射器
- 基本映射语句
- @INSERT
- @UPDATE
- @DELETE
- @SELECT
- 结果集映射
- 关系映射
- 一对一关系
- 一对多关系
- 动态SQL
- @InsertProvider
- @UpdateProvider
- @DeleteProvider
- @SelectProvider
- MyBatis与Spring,Struts2整合
- Spring与Struts整合
- Spring与MyBatis整合
具体介绍查看百度百科,或者直接查看官网介绍。
我们将采用最原始的JavaProject来构建项目,已达到最原始的使用体验,具体的构建过程不再赘述,所有内容将围绕MyBatis展开,一下所有方式均采用最佳实践,不常用的方法将不再赘述
开始之前,我们需要准备一个MySql数据库,并新建一张 t_student 表供测试用,后续我们的表结构会慢慢发生变化,并且表的数量也会增加
CREATE DATABASE DEFAULT CHARACTER SET utf8 ;USE `db_mybatis`;DROP TABLE IF EXISTS `t_student`;CREATE TABLE `t_student` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`age` int(11) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;insert into `t_student`(`id`,`name`,`age`) values(1,'张三',10),(2,'李四',11),(3,'李四',11),(4,'李四',11),(5,'李四',11),(6,'李四',11),(7,'李四',11);
然后新建一个JavaProject MB_01,项目结构如下:
MB_01--src--com.sue.demo--mappers--StudentMapper.java--StudentMapper.xml--model--Studnet.java--service--Test.java--util--SqlSessionFactoryUtil.java--lib--mysql-connector-java-3.1.12-bin.jar--mybatis-3.2.8.jar--jdbc.properties--mybatis-config.xml
配置 jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/db_mybatisjdbc.username=rootjdbc.password=
配置 mybaties-config.xml ,该配置文件中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务范围和控制方式的事务管理器(TransactionManager)。XML 配置文件的详细内容后面再探讨,这里先给出一个简单的示例:
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><properties resource="jdbc.properties"></properties><typeAliases><package name="com.sue.mybatis.demo.model"/></typeAliases><environments default="developer"><environment id="developer"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><mappers><package name="com.sue.mybatis.demo.mapper"/></mappers></configuration>
当然,还有很多可以在XML 文件中进行配置,上面的示例指出的则是最关键的部分。要注意 XML 头部的声明,用来验证 XML 文档正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则是包含一组 mapper 映射器(这些 mapper 的 XML 文件包含了 SQL 代码和映射定义信息)。
创建 Student.java
public class Student {private Integer id;private String name;private Integer age;public Student() {super();}public Student(String name, Integer age) {super();this.name = name;this.age = age;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}}
为了方便测试,不用每次都书写重复获取 SqlSession 的代码,我们创建 SqlSessionFactoryUtils.java 用于获取SqlSession
import java.io.IOException;import java.io.InputStream;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;public class SqlSessionFactoryUtils {public static SqlSessionFactory sqlSessionFactory;public static SqlSessionFactory getSqlSessionFactory(){if(sqlSessionFactory == null){InputStream is = null;try {is = Resources.getResourceAsStream("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);} catch (IOException e) {e.printStackTrace();}}return sqlSessionFactory;}public static SqlSession opSession(){return getSqlSessionFactory().openSession();}}
创建 StudentMapper.java (后面基本每建一张表,我们就会新增一个对应的POJO类)
import com.sue.mybatis.demo.model.Student;public interface StudentMapper {public int add(Student student);}
创建 StudentMapper.xml
<mapper namespace="com.sue.mybatis.demo.mapper.StudentMapper"> namespace 对应唯一的Interface接口
<insert id="add"> id 对应上述接口中的唯一方法
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.sue.mybatis.demo.mapper.StudentMapper"><insert id="add" parameterType="Student" useGeneratedKeys="true" keyProperty="id">insert into t_student(name, age) values(#{name}, #{age})</insert></mapper>
创建测试类Test.java,并运行
import org.apache.ibatis.session.SqlSession;import com.sue.mybatis.demo.mapper.StudentMapper;import com.sue.mybatis.demo.model.Student;import com.sue.mybatis.demo.util.SqlSessionFactoryUtils;public class Test {public static void main(String[] args) {SqlSession sqlSession = SqlSessionFactoryUtils.opSession();StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student s = new Student("wangwu---", 11);studentMapper.add(s);sqlSession.commit();System.out.println(s.getId());}}
以下讲解均是相对于mybatis-config.xml中的标签
该标签下可以存在多个 environment ,实际开发一般都存在3个环境:开发环境(dev),生产环境(pro),测试环境(test),这时可以在 environments 中配置3个 environment,通过 environments 的 default 属性指定所用环境
<environments default="developer"><environment id="developer"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment><environment id="production"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment><environment id="test"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments>
MyBatis支持2种类型是事物管理器:JDBC 和 MANAGED(托管)
<transactionManager type="JDBC" />
数据源配置,类型有:UNPOOLED,POOLED,JNDI
UNPOOLED:没有连接池,每次数据库操作,MyBatis都会创建一个新的连接,用完后关闭该连接。适合小并发的项目POOLED:连接池JNDI:使用应用服务器配置JNDI数据源获取数据库连接
<dataSource type="POOLED"><property name="driver" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource>
属性配置,建议采用下面写法
<properties resource="jdbc.properties"></properties>
给类的完全限定名取别名,建议采用下面写法
<typeAliases><package name="com.sue.mybatis.demo.model"/></typeAliases>
引入映射文件,建议采用下面写法
<mappers><package name="com.sue.mybatis.demo.mapper"/></mappers>
这是我喜欢MyBatis的地方之一,结合动态SQL我可以完全管理自己的SQL语句,再也不同担心Hibernate复杂的关系了,语句调优难问题了
开始之前,我们先在 StudentMapper.java 中新增几个常用方法,后面每一小节只需着重针 StudentMapper.xml 的配置和测试用例即可
public interface StudentMapper {public int add(Student student);public int update(Student student);public int delete(Integer id);public Student findById(Integer id);public List<Student> findAll();}
<insert id="add" parameterType="Student" useGeneratedKeys="true" keyProperty="id">insert into t_student(name ,age) values (#{name} ,#{age})</insert>
<update id="update" parameterType="Student">update t_student set name=#{name},age=#{age}where id=#{id}</update>
<delete id="delete" parameterType="Integer">delete from t_student where id=#{id}</delete>
在 findAll 中返回的集合可以用到 resultMap,此处先熟悉,后续复杂返回需要频繁使用,这个标签将伴随着你的开发生涯
<resultMap type="Student" id="StudentResult"><id property="id" column="id"/><result property="name" column="name"/><result property="age" column="age"/></resultMap>
<select id="findById" parameterType="Integer" resultType="Student">select * from t_student where id=#{id}</select><select id="findAll" resultMap="StudentResult">select * from t_student</select>
public class StudentTest2 {private SqlSession sqlSession = null;private StudentMapper sm = null;@Beforepublic void setUp() throws Exception {sqlSession = SqlSessionFactoryUtil.openSession();sm = sqlSession.getMapper(StudentMapper.class);}@Testpublic void testAdd() {Student s = new Student("李四", 12);sm.add(s);sqlSession.commit();}@Testpublic void testUpdate(){Student s = new Student(3,"李四3", 3);sm.update(s);sqlSession.commit();}@Testpublic void testDelete(){sm.delete(5);sqlSession.commit();}@Testpublic void testFindById(){Student s = sm.findById(2);System.out.println(s);}@Testpublic void testFindAll(){List<Student> stuList = sm.findAll();for(Student s : stuList){System.out.println(s);}}@Afterpublic void tearDown() throws Exception {sqlSession.close();}}
对象间映射方式很多,以下方式都是采用的最佳实践,其余实现方式不予讲解,因为实际开发中不常用
新需求总是不停的出现,某天老板让我们在查询学生信息的时候同时显示出该学生的地址信息。于是我就想到了一对一关系映射。
为了演示一对一在MyBatis中的使用,我们需要修改一下表结构:
t_address 表t_student 表添加 addressId 字段
DROP TABLE IF EXISTS `t_address`;CREATE TABLE `t_address` (`id` int(11) NOT NULL AUTO_INCREMENT,`sheng` varchar(20) DEFAULT NULL,`shi` varchar(20) DEFAULT NULL,`qu` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;DROP TABLE IF EXISTS `t_student`;CREATE TABLE `t_student` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`age` int(11) DEFAULT NULL,`addressId` int(11) DEFAULT NULLPRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
为 Student.java 添加 address 属性,并生成 getter()/setter() 方法, 同时更新 toString() 方法
private Address address;public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}
新增 Address.java 类
public class Address {private int id;private String sheng;private String shi;private String qu;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getSheng() {return sheng;}public void setSheng(String sheng) {this.sheng = sheng;}public String getShi() {return shi;}public void setShi(String shi) {this.shi = shi;}public String getQu() {return qu;}public void setQu(String qu) {this.qu = qu;}@Overridepublic String toString() {return "Address [id=" + id + ", sheng=" + sheng + ", shi=" + shi+ ", qu=" + qu + "]";}}
新增 AddressMapper.java 类
import com.sue.demo.model.Address;public interface AddressMapper {public Address findById(Integer id);}
新增 AddressMapper.xml 文件
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.sue.demo.mappers.AddressMapper"><resultMap type="Address" id="AddressResult"><result property="id" column="id"/><result property="sheng" column="sheng"/><result property="shi" column="shi"/><result property="qu" column="qu"/></resultMap><select id="findById" parameterType="Integer" resultType="Address">select * from t_address where id=#{id}</select></mapper>
修改 StudentMapper.java,新增方法 findStudentWithAddress 用来查询学生和学生的地址信息
public Student findStudentWithAddress(Integer id);
修改 StudentMapper.xml 文件:修改 StudentResult 并添加 findStudentWithAddress 查询
<resultMap type="Student" id="StudentResult"><id property="id" column="id"/><result property="name" column="name"/><result property="age" column="age"/><association property="address" column="addressId" select="com.sue.demo.mappers.AddressMapper.findById" /></resultMap><select id="findByIdWithAddress" parameterType="Integer" resultMap="StudentResult">select * from t_student t1 ,t_address t2 where t1.addressId=t2.id and t1.id=#{id}</select>
测试案例
@Testpublic void testFindByIdWithAddress(){Student student = sm.findByIdWithAddress(4);System.out.println(student);}
过了几天老板又提了一个新需求,需要根据年级查出该年级中的所有学生。
首先我们需要新增一张 t_grade 表,同时修改 t_student 表
DROP TABLE IF EXISTS `t_grade`;CREATE TABLE `t_grade` (`id` int(11) NOT NULL AUTO_INCREMENT,`gradeName` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;DROP TABLE IF EXISTS `t_student`;CREATE TABLE `t_student` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`age` int(11) DEFAULT NULL,`addressId` int(11) DEFAULT NULL,`gradeId` int(11) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
新增 Grade.java 类
import java.util.List;public class Grade {private int id;private String gradeName;private List<Student> students;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getGradeName() {return gradeName;}public void setGradeName(String gradeName) {this.gradeName = gradeName;}public List<Student> getStudents() {return students;}public void setStudents(List<Student> students) {this.students = students;}@Overridepublic String toString() {return "Grade [id=" + id + ", gradeName=" + gradeName + ", students="+ students + "]";}}
新增 GradeMapper.java 类
import com.sue.demo.model.Grade;public interface GradeMapper {public Grade findById(int id);}
新增 GradeMapper.xml 文件
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.sue.demo.mappers.GradeMapper"><resultMap type="Grade" id="GradeResult"><id property="id" column="id"/><result property="gradeName" column="gradeName"/><collection property="students" column="id" select="com.sue.demo.mappers.StudentMapper.findByGradeId"></collection></resultMap><select id="findById" parameterType="Integer" resultMap="GradeResult">select * from t_grade where id=#{id}</select></mapper>
修改 StudentMapper.java,新增方法 findByGradeId 方法
public Student findByGradeId(int id);
修改 StudentMapper.xml 文件,添加查询语句
<select id="findByGradeId" parameterType="Integer" resultMap="StudentResult">select * from t_student where gradeId = #{gradeId}</select>
测试案例
import org.apache.ibatis.session.SqlSession;import org.junit.After;import org.junit.Before;import org.junit.Test;import com.sue.demo.mappers.GradeMapper;import com.sue.demo.model.Grade;import com.sue.demo.util.SqlSessionFactoryUtil;public class GradeTest {private SqlSession sqlSession = null;private GradeMapper gm = null;@Beforepublic void setUp() throws Exception {sqlSession = SqlSessionFactoryUtil.openSession();gm = sqlSession.getMapper(GradeMapper.class);}@Testpublic void testFindById() {Grade grade = gm.findById(1);System.out.println(grade);sqlSession.commit();}@Afterpublic void tearDown() throws Exception {sqlSession.close();}}
好不容易实现了新的需求,还没等喝口水的时间,老板又告诉你需要在查询学生信息的时候关联查出年级信息。我的天呢~ 能不能一口气说完。OK,不要抱怨了,开始吧!
为 Student.java 添加 grade 属性,并生成 getter()/setter() 方法, 同时更新 toString() 方法
private Grade grade;public Grade getGrade() {return grade;}public void setGrade(Grade grade) {this.grade = grade;}
修改 StudentMapper.xml
<resultMap type="Student" id="StudentResult"><id property="id" column="id"/><result property="name" column="name"/><result property="age" column="age"/><association property="address" column="addressId" select="com.sue.demo.mappers.AddressMapper.findById" /><association property="grade" column="gradeId" select="com.sue.demo.mappers.GradeMapper.findById" /></resultMap>
测试案例 Student.java 中增加如下案例
@Testpublic void testFindByIdWithGrade(){Student student = sm.findByIdWithAddress(4);System.out.println(student);}
如果你所有步骤都是按照文档来的话,此时应该会抛出 StackOverflowError 异常
java.lang.StackOverflowErrorat java.util.ArrayList$Itr.<init>(ArrayList.java:820)at java.util.ArrayList$Itr.<init>(ArrayList.java:820)at java.util.ArrayList.iterator(ArrayList.java:814)at java.util.AbstractCollection.toString(AbstractCollection.java:450)at java.lang.String.valueOf(String.java:2847)at java.lang.StringBuilder.append(StringBuilder.java:128)at com.sue.demo.model.Grade.toString(Grade.java:38)at java.lang.String.valueOf(String.java:2847)at java.lang.StringBuilder.append(StringBuilder.java:128)at com.sue.demo.model.Student.toString(Student.java:71)at java.lang.String.valueOf(String.java:2847)at java.lang.StringBuilder.append(StringBuilder.java:128)........
这是因为 Student.java 和 Grade.java 中 toString 方法 循环调用引起的,我们可以通过将 Grade.java 中的 Student 信息暂时不打印来解决这问题,Grade 中 toString 方法修改如下
@Overridepublic String toString() {return "Grade [id=" + id + ", gradeName=" + gradeName + "]";}
此时在运行单元测试,将会愉快的看到你期望的输出
此节内容主要针对SQL(即xml),所以我们关注的对象为 XXXMapper.java 和 XXXMapper.xml
在开始之前,我们需要做如下准备:
HelloWorld 项目并重命名为 MB_02StudentMapper.java,StudentMapper.xml,Test.java 代码
MB_02--src--com.sue.demo--mappers--StudentMapper.java--StudentMapper.xml--model--Studnet.java--service--Test.java--util--SqlSessionFactoryUtil.java--lib--mysql-connector-java-3.1.12-bin.jar--mybatis-3.2.8.jar--jdbc.properties--mybatis-config.xml
StudentMapper.java 中新增查询方法 searchStudents
import java.util.List;import java.util.Map;import com.sue.demo.model.Student;public interface StudentMapper {public List<Student> searchStudents(Map<String, Object> map);}
StudentMapper.xml 中添加相应实现
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.sue.demo.mappers.StudentMapper"><resultMap type="Student" id="StudentResult"><id property="id" column="id"/><result property="name" column="name"/><result property="age" column="age"/></resultMap><select id="searchStudents" parameterType="Map" resultMap="StudentResult">select * from t_student where 1=1<if test="name != null">and name like #{name}</if><if test="age != null">and age = #{age}</if></select></mapper>
测试案例
import java.util.HashMap;import java.util.List;import java.util.Map;import org.apache.ibatis.session.SqlSession;import org.junit.After;import org.junit.Before;import org.junit.Test;import com.sue.demo.mappers.StudentMapper;import com.sue.demo.model.Student;import com.sue.demo.util.SqlSessionFactoryUtil;public class StudentTest {private SqlSession sqlSession = null;private StudentMapper sm = null;@Beforepublic void setUp() throws Exception {sqlSession = SqlSessionFactoryUtil.openSession();sm = sqlSession.getMapper(StudentMapper.class);}@Testpublic void testSearchStudents(){Map<String, Object> map = new HashMap<String, Object>();map.put("name", "%zhang%");map.put("age", 10);List<Student> students = sm.searchStudents(map);for(Student s : students){System.out.println(s);}}@Afterpublic void tearDown() throws Exception {sqlSession.close();}}
假如有一个需求:页面有一个选择框,可以选择班级ID 学生名 ,如果不选择默认表示年龄,有一个输入框负责输入数据,通过这种方式来进行查询,那么后台就需要动态的拼接SQL,我们只需要将上一个栗子改造一下即可实现效果
在 StudentMapper.java 中新增查询方法 searchStudents1
public List<Student> searchStudents1(Map<String, Object> map);
在 StudentMapper.xml 中新增对应查询
<select id="searchStudents1" parameterType="Map" resultMap="StudentResult">select * from t_student<choose><when test="searchBy == 'gradeId'">where gradeId = #{gradeId}</when><when test="searchBy == 'name'">where name like #{name}</when><otherwise>where age = #{age}</otherwise></choose></select>
测试案例
@Testpublic void testSearchStudents1(){Map<String, Object> map = new HashMap<String, Object>();map.put("searchBy", "name");map.put("name", "%zhang%");map.put("age", 11);List<Student> students = sm.searchStudents1(map);for(Student s : students){System.out.println(s);}}
where 标签可以用 where 1=1 代替,可行但是不推荐,虽然写法简单,但是效率肯定是存在问题的
使用 where 标签的好处
1. 自动加上where
2. 如果where子句以and或者or开头,则自动删除第一个and或者or
同样还是修改第一个栗子
在 StudentMapper.java 中新增查询方法 searchStudents2
public List<Student> searchStudents2(Map<String, Object> map);
在 StudentMapper.xml 中新增对应查询
<select id="searchStudents2" parameterType="Map" resultMap="StudentResult">select * from t_student<where><if test="name != null">and name like #{name}</if><if test="age != null">and age = #{age}</if></where></select>
测试案例
@Testpublic void testSearchStudents2(){Map<String, Object> map = new HashMap<String, Object>();map.put("name", "%zhang%");map.put("age", 10);List<Student> students = sm.searchStudents2(map);for(Student s : students){System.out.println(s);}}
功能和 where 元素类似,提供了 前缀 和 后缀 功能,相比更加灵活
同样还是修改第一个栗子
在 StudentMapper.java 中新增查询方法 searchStudents3
public List<Student> searchStudents3(Map<String, Object> map);
在 StudentMapper.xml 中新增对应查询
<select id="searchStudents3" parameterType="Map" resultMap="StudentResult">select * from t_student<trim prefix="where" prefixOverrides="and|or"><if test="name != null">and name like #{name}</if><if test="age != null">and age = #{age}</if></trim></select>
测试案例
@Testpublic void testSearchStudents3(){Map<String, Object> map = new HashMap<String, Object>();map.put("name", "%zhang%");map.put("age", 10);List<Student> students = sm.searchStudents3(map);for(Student s : students){System.out.println(s);}}
我们经常会写一些类似于 select id,age,name from t_student where gradeId in(1,2) 的语句,对于in的拼接,可以使用foreach元素
同样还是修改第一个栗子
在 StudentMapper.java 中新增查询方法 searchStudents4
public List<Student> searchStudents4(Map<String, Object> map);
在 StudentMapper.xml 中新增对应查询
<select id="searchStudents4" parameterType="Map" resultMap="StudentResult">select * from t_student<if test="gradeIds != null"><where>gradeId in<foreach collection="gradeIds" item="gradeId" open="(" separator="," close=")">#{gradeId}</foreach></where></if></select>
测试案例
@Testpublic void testSearchStudents4(){Map<String, Object> map = new HashMap<String, Object>();List<Integer> gradeIds = new ArrayList<Integer>();gradeIds.add(1);gradeIds.add(2);map.put("gradeIds", gradeIds);List<Student> students = sm.searchStudents4(map);for(Student s : students){System.out.println(s);}}
使用 set 的好处:
1. 自动加上 set
2. 自动删除最后一个逗号,
StudentMapper.java 中新增修改方法 updateStudent
public int updateStudent(Student student);
StudentMapper.xml 中添加相应实现
<update id="updateStudent" parameterType="Student">update t_student<set><if test="name != null">name = #{name},</if><if test="age != null">age = #{age},</if></set>where id = #{id}</update>
测试案例
@Testpublic void testUpdateStudents(){Student student = new Student();student.setId(1);student.setName("zhangsan-update");int i = sm.updateStudent(student);sqlSession.commit();if(i > 0) System.out.println("更新成功");}
掌握以上知识点,开发应该不会有太大的问题。本章主要讲解一些MyBatis的边角知识,平时使用频率不高,了解会用即可
新建 t_other 表
DROP TABLE IF EXISTS `t_other`;CREATE TABLE `t_other` (`id` int(11) NOT NULL AUTO_INCREMENT,`pic` longblob,`remark` longtext,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
新建 Other.java 类
import java.util.Arrays;public class Other {private int id;private byte[] pic;private String remark;public int getId() {return id;}public void setId(int id) {this.id = id;}public byte[] getPic() {return pic;}public void setPic(byte[] pic) {this.pic = pic;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}@Overridepublic String toString() {return "Other [id=" + id + ", remark=" + remark + "]";}}
新建 OtherMapper.java
import com.sue.demo.model.Other;public interface OtherMapper {public int insertOther(Other other);public Other getOtherById(int id);}
新建 OtherMapper.xml
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.sue.demo.mappers.OtherMapper"><insert id="insertOther" parameterType="Other">insert into t_other values(null,#{pic},#{remark})</insert><select id="getOtherById" parameterType="int" resultType="Other">select * from t_other where id = #{id}</select></mapper>
新增测试案例 OtherTest.java
import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.io.OutputStream;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.junit.After;import org.junit.Before;import org.junit.Test;import com.sue.demo.mappers.OtherMapper;import com.sue.demo.model.Other;import com.sue.demo.util.SqlSessionFactoryUtil;public class OtherTest {private SqlSession sqlSession = null;private OtherMapper om = null;@Beforepublic void setUp() throws Exception {sqlSession = SqlSessionFactoryUtil.openSession();om = sqlSession.getMapper(OtherMapper.class);}@Testpublic void testInsertOther(){Other other = new Other();other.setRemark("这是一篇文章,很长很长。");byte[] pic = null;try{InputStream is = Resources.getResourceAsStream("longze.jpg");pic = new byte[is.available()];is.read(pic);is.close();}catch(Exception e){e.printStackTrace();}other.setPic(pic);int i = om.insertOther(other);sqlSession.commit();if(i > 0) System.out.println("插入成功");}@Testpublic void testGetOtherById(){Other other = om.getOtherById(2);byte[] pic = other.getPic();try{File file = new File("temp.jpg");OutputStream os = new FileOutputStream(file);os.write(pic);os.close();}catch(Exception e){e.printStackTrace();}}@Afterpublic void tearDown() throws Exception {sqlSession.close();}}
比如数据库有100条数据,我们需要每页显示10条,将100条一次性加载到内存,再取10条进行分页展示,数据量一大,性能明显低
在 StudentMapper.java 中新增查询方法 findStudents
public List<Student> findStudents(RowBounds rowBounds);
在 StudentMapper.xml 中新增查询语句
<select id="findStudents" resultMap="StudentResult">select * from t_student</select>
在 StudentTest.java 中新增测试案例
@Testpublic void testFindStudents(){int offset = 0;int limit = 2;RowBounds rowBounds = new RowBounds(offset, limit);List<Student> students = sm.findStudents(rowBounds);for(Student s : students){System.out.println(s);}}
直接从数据库查询出你需要的数据,应用多,效率高
在 StudentMapper.java 中新增查询方法 findStudents1
public List<Student> findStudents1(Map<String, Object> map);
在 StudentMapper.xml 中新增查询语句
<select id="findStudents1" parameterType="Map" resultMap="StudentResult">select * from t_student<if test="start != null and size != null">limit #{start}, #{size}</if></select>
在 StudentTest.java 中新增测试案例
@Testpublic void testFindStudents1(){Map<String, Object> map = new HashMap<String, Object>();map.put("start", 0);map.put("size", 2);List<Student> students = sm.findStudents1(map);for(Student s : students){System.out.println(s);}}
默认情况下,MyBatis 启用一级缓存,即同一个 SqlSession 接口对象调用了相同的 SELECT 语句,则直接会从缓存中返回结果,而不是再查询一次数据库
开发者可以自己配置二级缓存,二级缓存是全局的
默认情况下,SELECT 是使用缓存的,insert update delete 是不使用缓存的
启用缓存的话,需要先配置缓存,配置在Mapper文件的 <mapper> 元素下
<!--1,size:表示缓存cache中能容纳的最大元素数。默认是1024;2,flushInterval:定义缓存刷新周期,以毫秒计;3,eviction:定义缓存的移除机制;默认是LRU(least recently userd,最近最少使用),还有FIFO(first in first out,先进先出)4,readOnly:默认值是false,假如是true的话,缓存只能读。--><cache size="1024" flushInterval="60000" eviction="LRU" readOnly="false"/>
启用缓存的话,需要在具体的 select insert update 中开启
<select id="findStudents" resultMap="StudentResult" useCache="true">select * from t_student</select><update id="updateStudent" parameterType="Student" flushCache="true">update t_student<set><if test="name != null">name = #{name},</if><if test="age != null">age = #{age},</if></set>where id = #{id}</update>
实际项目开发中,很少使用,一般缓存都是通过第三方组件解决
本节内容可添加装逼属性,请选择性观看,MyBatis该特性可以省略 XXMapper.xml 文件的编写,带来开发上的体验(纯属装逼),但同时带来的弊端是不利于SQL的维护。实际开发中几乎没有这么使用的,除非项目经理脑子疼或者被驴踢过。
这节将会带领大家实现一个基于注解的 增 改 删 查 案例,首先我们可以复制 MB_02 项目,并重命名为 MB_03 ,同时删除无用的类,最终项目结构和代码清单如下:
MB_03--src--com.sue.demo--mappers--StudentMapper.java--model--Studnet.java--service--StudentTest.java--util--SqlSessionFactoryUtil.java--lib--mysql-connector-java-3.1.12-bin.jar--mybatis-3.2.8.jar--jdbc.properties--mybatis-config.xml
Student.java
public class Student {private int id;private String name;private int age;public Student() {super();}public Student(int id, String name, int age) {super();this.id = id;this.name = name;this.age = age;}public Student(String name, int age) {super();this.name = name;this.age = age;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";}}
StudentMapper.java
import java.util.List;import com.sue.demo.model.Student;public interface StudentMapper {public int updateStudent(Student student);public int insertStudent(Student student);public Student getStudentById(int id);public int deleteStudentById(int id);}
SqlSessionFactoryUtil.java
import java.io.IOException;import java.io.InputStream;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;public class SqlSessionFactoryUtil {public static SqlSessionFactory sqlSessionFactory;public static SqlSessionFactory getSqlSessionFactory(){if(sqlSessionFactory == null){InputStream is = null;try {is = Resources.getResourceAsStream("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);} catch (IOException e) {e.printStackTrace();}}return sqlSessionFactory;}public static SqlSession openSession(){return getSqlSessionFactory().openSession();}}
StudentTest.java
import org.apache.ibatis.session.SqlSession;import org.junit.After;import org.junit.Before;import org.junit.Test;import com.sue.demo.mappers.StudentMapper;import com.sue.demo.util.SqlSessionFactoryUtil;public class StudentTest {private SqlSession sqlSession = null;private StudentMapper sm = null;@Beforepublic void setUp() throws Exception {sqlSession = SqlSessionFactoryUtil.openSession();sm = sqlSession.getMapper(StudentMapper.class);}@Testpublic void testSearchStudents(){}@Afterpublic void tearDown() throws Exception {sqlSession.close();}}
做好以上准备,我们就可以愉快的开始装逼旅程了.
StudentMapper.java 中为 insertStudent 方法添加 @Insert 注解
@Insert("insert into t_student values(null, #{name}, #{age})")public int insertStudent(Student student);
测试案例
@Testpublic void testInsertStudent(){Student student = new Student("xiaoming", 20);sm.insertStudent(student);sqlSession.commit();}
StudentMapper.java 中为 updateStudent 方法添加 @Update 注解
@Update("update t_student set name=#{name}, age=#{age} where id=#{id}")public int updateStudent(Student student);
测试案例
@Testpublic void testUpdateStudent(){Student student = new Student("xiaoming-up", 21);student.setId(4);sm.updateStudent(student);sqlSession.commit();}
StudentMapper.java 中为 deleteStudentById 方法添加 @Delete 注解
@Delete("delete from t_student where id=#{id}")public int deleteStudentById(int id);
测试案例
@Testpublic void testDeleteStudentById(){sm.deleteStudentById(4);sqlSession.commit();}
StudentMapper.java 中为 getStudentById 方法添加 @Select 注解
@Select("select * from t_student where id=#{id}")public Student getStudentById(int id);
测试案例
@Testpublic void testGetStudentById(){Student student = sm.getStudentById(3);System.out.println(student);}
了解了基本的 增 删 改 查 操作后,我们再来看一个结果集映射。
在使用 xml 进行sql定义的时候,我们的 resultMap 属性对应着一个自定义的 <resultMap> 集合,在注解的方式中,我们需要使用到 @Results 和 @Result 注解来完成这种关系的映射
StudentMapper.java 中为 deleteStudentById 方法添加 @Delete 注解
@Delete("delete from t_student where id=#{id}")public int deleteStudentById(int id);
测试案例
@Testpublic void testDeleteStudentById(){sm.deleteStudentById(4);sqlSession.commit();}
StudentMapper.java 中为 findStudents 方法添加 @Select @Results @Result 注解
@Select("select * from t_student")@Results({@Result(id=true, column="id", property="id"),@Result(column="name", property="name"),@Result(column="age", property="age"),})public List<Student> findStudents();
测试案例
@Testpublic void testFindStudents(){List<Student> students = sm.findStudents();for(Student s : students){System.out.println(s);}}
从 MyBatis关系映射 > 一对一关系 这一讲中,将 Address.java 和 AddressMapper.java 复制到 MB_03 项目中
在Student.java 中添加 Address 属性 ,生成 getter/setter 方法,同时更新 toString() 方法
private Address address;public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}
在 StudentMapper.java 中新增 selectStudentWithAddress 方法,同时添加 @Select @Results @Result @One 注解
@Select("select * from t_student where id=#{id}")@Results({@Result(id=true, column="id", property="id"),@Result(column="name", property="name"),@Result(column="age", property="age"),@Result(column="addressId", property="address", one=@One(select="com.sue.demo.mappers.AddressMapper.findByid"))})public Student selectStudentWithAddress(int id);
在 AddressMapper.java 中新增 findByid 方法,同时添加 @Select 注解
@Select("select * from t_address where id=#{id}")public Address findByid(int id);
测试案例
@Testpublic void testSelectStudentWithAddress(){Student student = sm.selectStudentWithAddress(3);System.out.println(student);}
从 MyBatis关系映射 > 一对一关系 这一讲中,将 Grade.java 和 GradeMapper.java 复制到 MB_03 项目中
注意 Grade.java 需生成完整的 getter/setter 方法并 toString 所有属性
在 Student.java 中添加 Grade 属性 ,生成 getter/setter 方法,同时更新 toString() 方法[请不要输出 grade 属性]
private Grade grade;public Grade getGrade() {return grade;}public void setGrade(Grade grade) {this.grade = grade;}@Overridepublic String toString() {return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";}
在 GradeMapper.java 中为 findById 方法添加 @Select @Results @Result @Many 注解
@Select("select * from t_grade where id=#{id}")@Results({@Result(id=true, column="id", property="id"),@Result(column="gradeName", property="gradeName"),@Result(column="id", property="students", many=@Many(select="com.sue.demo.mappers.StudentMapper.selectStudentByGradeId"))})public Grade findById(int id);
在 StudentMapper.java 中新增 selectStudentByGradeId 方法,同时添加 @Select @Results @Result @One 注解
@Select("select * from t_student where gradeId=#{id}")@Results({@Result(id=true, column="id", property="id"),@Result(column="name", property="name"),@Result(column="age", property="age"),@Result(column="addressId", property="address", one=@One(select="com.sue.demo.mappers.AddressMapper.findByid"))})public Student selectStudentByGradeId(int id);
测试案例,新建 GradeTest.java
import org.apache.ibatis.session.SqlSession;import org.junit.After;import org.junit.Before;import org.junit.Test;import com.sue.demo.mappers.GradeMapper;import com.sue.demo.model.Grade;import com.sue.demo.util.SqlSessionFactoryUtil;public class GradeTest {private SqlSession sqlSession = null;private GradeMapper gm = null;@Beforepublic void setUp() throws Exception {sqlSession = SqlSessionFactoryUtil.openSession();gm = sqlSession.getMapper(GradeMapper.class);}@Testpublic void testfindById(){Grade grade = gm.findById(1);System.out.println(grade);}@Afterpublic void tearDown() throws Exception {sqlSession.close();}}
换个思路,如果我们想通过查询学生带出年级信息,又该怎么做呢?这也是在开发中很常用的反向的对应关系,下面我们就做一个实例来看一下应该怎么实现
基于上述代码,在 StudentMapper.java 中新增 selectStudentWithAddressAndGrade 方法,同时添加 @Select @Results @Result @One 注解
@Select("select * from t_student where id=#{id}")@Results({@Result(id=true, column="id", property="id"),@Result(column="name", property="name"),@Result(column="age", property="age"),@Result(column="addressId", property="address", one=@One(select="com.sue.demo.mappers.AddressMapper.findByid")),@Result(column="gradeId", property="grade", one=@One(select="com.sue.demo.mappers.GradeMapper.findById"))})public Student selectStudentWithAddressAndGrade(int id);
更新 Student.java 的 toString() 方法,输出所有字段,为了防止内存溢出,我们还需要将 Grade.java 的 toString() 方法中的 student 属性去掉,不做输出。
测试案例
@Testpublic void testSelectStudentWithAddressAndGrade(){Student student = sm.selectStudentWithAddressAndGrade(1);System.out.println(student);}
该章作为了解即可,实际开发中还是用 xml 的方式进行开发,在开发前我们先在 MB_03 中新建一个演示类 StudentModel.java ,一个动态sql提供类 StudentDynamicSqlProvider.java 用来构建动态sql,一个Mapper类 StudentModelMapper.java,和一个测试类 StudentModelTest.java ,清单如下:
StudentModel.java
package com.sue.demo.model;public class StudentModel {private int id;private String name;private Integer age;public StudentModel() {}public StudentModel(String name, int age) {this.name = name;this.age = age;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "StudentModel [id=" + id + ", name=" + name + ", age=" + age+ "]";}}
StudentModelMapper.java
package com.sue.demo.mappers;import com.sue.demo.model.StudentModel;public interface StudentModelMapper {public int insertStudentModel(StudentModel studentModel);public int updateStudentModel(StudentModel studentModel);public int deleteStudentModelById(int id);public StudentModel getStudentModelById(int id);}
StudentDynamicSqlProvider.java
空:下面会慢慢补充
StudentModelTest.java
package com.sue.demo.service;import org.apache.ibatis.session.SqlSession;import org.junit.After;import org.junit.Before;import org.junit.Test;import com.sue.demo.mappers.StudentModelMapper;import com.sue.demo.model.StudentModel;import com.sue.demo.util.SqlSessionFactoryUtil;public class StudentModelTest {private SqlSession sqlSession = null;private StudentModelMapper sm = null;@Beforepublic void setUp() throws Exception {sqlSession = SqlSessionFactoryUtil.openSession();sm = sqlSession.getMapper(StudentModelMapper.class);}@Testpublic void testInsertStudentModel(){}@Testpublic void testUpdateStudent(){}@Testpublic void testDeleteStudentById(){}@Testpublic void testGetStudentById(){}@Afterpublic void tearDown() throws Exception {sqlSession.close();}}
在 StudentDynamicSqlProvider.java 中新建方法 insertStudent
public String insertStudentModel(final StudentModel studentModel){return new SQL(){{INSERT_INTO("t_student");if(studentModel.getName() != null){VALUES("name", "#{name}");}if(studentModel.getAge() != null){VALUES("age", "#{age}");}}}.toString();}
在 StudentModelMapper.java 的 insertStudent 方法上添加注解 @InsertProvider
@InsertProvider(type=StudentDynamicSqlProvider.class, method="insertStudentModel")public int insertStudentModel(StudentModel studentModel);
补充测试案例
@Testpublic void testInsertStudentModel(){StudentModel studentModel = new StudentModel("insert", 18);sm.insertStudentModel(studentModel);sqlSession.commit();}
在 StudentDynamicSqlProvider.java 中新建方法 insertStudent
public String updateStudentModel(final StudentModel studentModel){return new SQL(){{UPDATE("t_student");if(studentModel.getName() != null){SET("name=#{name}");}if(studentModel.getAge() !=null){SET("age=#{age}");}WHERE("id=#{id}");}}.toString();}
在 StudentModelMapper.java 的 updateStudentModel 方法上添加注解 @UpdateProvider
@UpdateProvider(type=StudentDynamicSqlProvider.class, method="updateStudentModel")public int updateStudentModel(StudentModel studentModel);
补充测试案例
@Testpublic void testUpdateStudent(){StudentModel studentModel = new StudentModel("insert-update", 20);studentModel.setId(5);sm.updateStudentModel(studentModel);sqlSession.commit();}
在 StudentDynamicSqlProvider.java 中新建方法 insertStudent
public String deleteStudentModel(){return new SQL(){{DELETE_FROM("t_student");WHERE("id=#{id}");}}.toString();}
在 StudentModelMapper.java 的 deleteStudentModelById 方法上添加注解 @DeleteProvider
@DeleteProvider(type=StudentDynamicSqlProvider.class, method="deleteStudentModel")public int deleteStudentModelById(int id);
补充测试案例
@Testpublic void testDeleteStudentById(){sm.deleteStudentModelById(5);sqlSession.commit();}
在 StudentDynamicSqlProvider.java 中新建方法 getStudentModelById
public String getStudentModelById(){return new SQL(){{SELECT("*");FROM("t_student");WHERE("id=#{id}");}}.toString();}
在 StudentModelMapper.java 的 getStudentModelById 方法上添加注解 @SelectProvider
@SelectProvider(type=StudentDynamicSqlProvider.class, method="getStudentModelById")public StudentModel getStudentModelById(int id);
补充测试案例
@Testpublic void testGetStudentById(){StudentModel studentModel = sm.getStudentModelById(1);System.out.println(studentModel);}
做完基本的 增 删 改 查 操作后,我们再来一个稍微复杂一点的
在 StudentDynamicSqlProvider.java 中新建方法 findStudentsByCondation
public String findStudentsByCondation(final Map<String, Object> param){return new SQL(){{SELECT("*");FROM("t_student");StringBuffer sb = new StringBuffer();if(param.get("name") != null){sb.append(" and name like '" + param.get("name") + "'");}if(param.get("age") != null){sb.append(" and age = " + param.get("age"));}WHERE(sb.toString().replaceFirst("and", ""));}}.toString();}
在 StudentModelMapper.java 的 findStudentsByCondation 方法上添加注解 @SelectProvider
@SelectProvider(type=StudentDynamicSqlProvider.class, method="findStudentsByCondation")public List<StudentModel> findStudentsByCondation(Map<String, Object> param);
补充测试案例
@Testpublic void findStudentsByCondation(){Map<String,Object> param = new HashMap<String,Object>();param.put("name", "%zhang%");param.put("age", 10);List<StudentModel> studentModels = sm.findStudentsByCondation(param);System.out.println(studentModels);}
该节内容请参照 MB_04 ,因网上教程太多,不再笔述