SSM使用之MyBatis

218 阅读13分钟

1.Mybatis是什么

  1. Mybatis 是一个优秀的基于 java 的持久层框架,内部封装了 jdbc,开发者只需要关注 sql 语句本身,而不需要处理加载驱动、创建连接、创建 statement、关闭连接,资源等繁杂的过程。 MyBatis 通过 xml 或注解两种方式将要执行的各种 sql 语句配置起来,并通过 java 对象和 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java对象并返回。

  2. Mybatis解决的主要问题:减轻使用 JDBC 的复杂性,不用编写重复的创建 Connetion, Statement ,不用编写关闭资源代码。直接使用 java 对象,表示结果数据。让开发者专注 SQL 的处理。 其他分心的工作由 MyBatis 代劳。

  3. Mybatis完成的事情:

    • 注册数据库的驱动
    • 创建 JDBC 中必须使用的 Connection , Statement, ResultSet 对象
    • 从 xml 中获取 sql,并执行 sql 语句,把 ResultSet 结果转换 java 对象
    • 关闭资源

2.Mybatis快速上手(第一个例子)

​ 说明:这个例子需要基于maven搭建的项目

  1. 创建一张表

    CREATE TABLE `student` (
     `id` int(11) NOT NULL ,
     `name` varchar(255) DEFAULT NULL,
     `email` varchar(255) DEFAULT NULL,
     `age` int(11) DEFAULT NULL,
     PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
  2. 加入maven依赖和插件

    <dependencies>
     <!-- 单元测试 -->
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.11</version>
         <scope>test</scope>
    </dependency>
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.1</version>
    </dependency>
     <!-- mysql驱动 -->
     <dependency>
     	<groupId>mysql</groupId>
     	<artifactId>mysql-connector-java</artifactId>
     	<version>5.1.9</version>
     </dependency>
    </dependencies>
    <build>
     <resources>
         <resource>
             <directory>src/main/java</directory><!--所在的目录-->
             <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
             <include>**/*.properties</include>
             <include>**/*.xml</include>
             </includes>
             <filtering>false</filtering>
         </resource>
    </resources>
     <plugins>
         <plugin>
             <artifactId>maven-compiler-plugin</artifactId>
             <version>3.1</version>
             <configuration>
                 <source>1.8</source>
                 <target>1.8</target>
             </configuration>
         </plugin>
    </plugins>
    
  3. 编写实体类

    package com.tbabs.pojo; 
    // 实体类Student
    public class Student {
         //属性名和列名一样 
         private Integer id;
         private String name;
         private String email;
         private Integer age;
     	// 下面是Getter和Setter方法(代码太多这里就不列出来了)
    }
    
  4. 编写Dao接口

    package com.tbabs.dao;
    import com.tbabs.pojo.Student;
    import java.util.List;
    
    public interface StudentDao {
     // 查询Studen表的所有数据
     List<Student> selectStudents();
    }
    
  5. 编写对应的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">
    <!--
     1. 配置文件的开头一定要写好
     2. namespace:必须有值,自定义的唯一字符串 
     3. 推荐使用:dao 接口的全限定名称 
    --> 
    <mapper namespace="com.tbabs.dao.StudentDao">
     <!--
     1. <select>: 查询数据, 标签中必须是 select 语句
     2. id: sql 语句的自定义名称,推荐使用 dao 接口中方法名称, 使用名称表示要执行的 sql 语句
    	select的id规范和dao接口中的方法名一致
     3. resultType: 查询语句的返回结果数据类型,使用全限定类名 
     -->
     <select id="selectStudents" resultType="com.tbabs.pojo.Student">
         <!--要执行的 sql 语句-->
         select id,name,email,age from student
     </select>
    </mapper>
    
  6. 编写Mybatis的主配置文件

    <!-- 
    	说明:
    	1.该文件属于mybatis的主配置文件,放在maven构建的项目的 src/main的resources 目录下
    	2.resources必须设置为resources root
    -->
    <?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">
    <!-- mybatis的日志功能 -->
    <settings>
     <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
    
    <configuration>
     <!--配置 mybatis 环境-->
     <environments default="mysql">
     <!--id:数据源的名称-->
     <environment id="mysql">
     <!--配置事务类型:使用 JDBC 事务(使用 Connection 的提交和回滚)-->
     <transactionManager type="JDBC"/>
     <!--
    	数据源 dataSource:创建数据库 Connection 对象
        type: POOLED 使用数据库的连接池 
     -->
     <dataSource type="POOLED">
     <!--连接数据库的四个要素-->
     <property name="driver" value="com.mysql.jdbc.Driver"/>
     <property name="url" value="jdbc:mysql://localhost:3306/<数据库名字>"/>
     <property name="username" value="<数据库用户名>"/>
     <property name="password" value="<数据库密码>"/>
     </dataSource>
     </environment>
     </environments>
     <mappers>
     <!--告诉 mybatis 要执行的 sql 语句的位置-->
     <mapper resource="com/tbabs/dao/StudentDao.xml"/>
     </mappers>
    </configuration>
    
  7. 创建测试类(src/test/java/com/tbabs/ 创建 MyBatisTest.java 文件)

    @Test
    public void testStart() throws IOException {
     //1.mybatis 主配置文件 
     String config = "mybatis-config.xml";
     //2.读取配置文件 
     InputStream in = Resources.getResourceAsStream(config);
     //3.创建 SqlSessionFactory 对象,目的是获取 SqlSession 
     SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
     //4.获取 SqlSession,SqlSession 能执行 sql 语句(这里无参设置事务手动提交)
     SqlSession session = factory.openSession();
     //5.执行 SqlSession 的 selectList()
     List<Student> studentList = session.selectList("com.tbabs.dao.StudentDao.selectStudents");
     //6.循环输出查询结果 
     studentList.forEach( student -> System.out.println(student));
     //7.关闭 SqlSession,释放资源
     session.close();
    }
    
    /*
        List<Student> studentList = session.selectList("com.bjpowernode.dao.StudentDao.selectStudents");
        近似等价的 jdbc 代码
        Connection conn = 获取连接对象
        String sql=” select id,name,email,age from student”
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
    */
    

3.Mybatis使用的对象分析

  1. 从刚刚的快速上手中使用到的Mybatis有SqlSession , SqlSessionFactory,接下来依次讲解。

  2. Resources类

    Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。

  3. SqlSessionFactoryBuilder 类

    SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 由于SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。

  4. SqlSessionFactory 接口

    SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用只需要一个该对象即可。创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法

    ➢ openSession(true):创建一个有自动提交功能的 SqlSession

    ➢ openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交

    ➢ openSession():同 openSession(false)

  5. SqlSession 接口

    SqlSession 接口对象用于执行持久化操作一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭。


4.Mybatis框架Dao代理

​ 抛弃传统的Dao接口实现类,直接定位到映射文件mapper中的SQL语句中,对数据库进行操作,这就是对Dao的实现方式,称为Mapper的动态代理方式。Mapper动态代理无需程序员实现Dao接口,接口是由Mybatis结合映射文件自动生成的动态代理实现的。

  1. getMapper获取代理对象

    只需调用SqlSession的getMapper()方法即可获取指定接口的实现类对象,该方法的参数为指定Dao接口类的class值

    SqlSession session = factory.openSession();
    // 参数为指定Dao接口类的class值
    StudentDao dao = session.getMapper(StudentDao.class);
    
  2. 使用Dao代理对象方法执行sql语句

    @Test
    public void testSelect() throws IOException {
     //1.mybatis 主配置文件 
     String config = "mybatis-config.xml";
     //2.读取配置文件 
     InputStream in = Resources.getResourceAsStream(config);
     //3.创建 SqlSessionFactory 对象,目的是获取 SqlSession 
     SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
     //4.获取 SqlSession,SqlSession 能执行 sql 语句
     SqlSession session = factory.openSession();
     //5.获取Dao代理对象
     StudentDao studentDao = session.getMapper(StudentDao.class);
     List<Student> studentList = studentDao.selectStudents();
     studentList.forEach( stu -> System.out.println(stu));
    }
    
  3. 原理

    动态代理(深入源码分析)


5.传入参数parameterType

​ parameterType: 接口中方法参数的类型,类型的完全限定名或别名。这个属性是可选的,因为 MyBatis可以推断出具体传入语句的参数,默认值为未设置(unset)。接口中方法的参数从 java 代码传入到mapper 文件的 sql 语句。

<delete id="deleteStudent" parameterType="int">
	delete from student where id=#{studentId}
</delete>
<delete id="deleteStudent" parameterType="java.lang.Integer">
 	delete from student where id=#{studentId}
</delete>
<!-- 这两者的参入参数类型是一致的 -->
5.1传入一个简单参数

(常用)Dao 接口中方法的参数只有一个简单类型(java 基本类型和 String),占位符 #{任意字符},和方法的参数名无关

//接口方法
Student selectById(int id);
<select id="selectById" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student where id=#{studentId}
</select>
<!--
	#{studentId} , studentId 是自定义的变量名称,和方法参数名无关。
-->
5.2传入多个参数(第一种方法-使用@Param)

(常用)当 Dao 接口方法多个参数,需要通过名称使用参数。在方法形参前面加入@Param(“自定义参数名”),mapper 文件使用#{自定义参数名}

// 接口文件
List<Student> selectMultiParam(@Param("personName") String name, @Param("personAge") int age);
<select id="selectMultiParam" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student where name = #{personName} or age = #{personAge}
</select>
<!-- 这里使用的时注解里的值作为标识当前参数,和方法参数名无关 -->
5.3传入多个参数(第二种方法-使用对象)

(常用)使用 java 对象传递参数, java 的属性值就是 sql 需要的参数值。 每一个属性就是一个参数。

​ 语法格式: #{ property,javaType=java 中数据类型名,jdbcType=数据类型名称 }

​ javaType和jdbcType 的类型 MyBatis 可以检测出来,一般不需要设置。常用格式 #{ property }

只需要写property 即可

//传入参数封装成对象
package com.tbabs.vo;
public class QueryParam {
 private String queryName;
 private int queryAge;
 //set ,get 方法
}
//接口方法
List<Student> selectMultiObject(QueryParam queryParam);
<!-- 简写(#{ property })推荐使用-->
<select id="selectMultiObject" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student where name = #{queryName} or age = #{queryAge}
</select>
<!-- 全部语法格式 -->
<select id="selectMultiObject" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student
 where name=#{queryName,javaType=string,jdbcType=VARCHAR}
 or age =#{queryAge,javaType=int,jdbcType=INTEGER}
</select>
5.4传入多个参数(第三种方法-按位置)

(不常用)参数位置从 0 开始, 引用参数语法 #{ arg位置 } , 第一个参数是#{arg0}, 第二个是#{arg1}

注意:mybatis-3.3 版本和之前的版本使用#{0},#{1}方式, 从 mybatis3.4 开始使用#{arg0}方式。

//接口方法
List<Student> selectByNameAndAge(String name,int age);
<select id="selectByNameAndAge" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student where name=#{arg0} or age =#{arg1}
</select>
<!-- 按位置传入参数进行标识不常用 -->
5.5传入多个参数(第四种方法-使用Map)

(不常用)Map 集合可以存储多个值,使用Map向 mapper 文件一次传入多个参数。Map 集合使用 String的 key,Object 类型的值存储参数。 mapper 文件使用 # { key } 引用参数值。

// 接口方法
List<Student> selectMultiMap(Map<String,Object> map);
// 传入的map可以是
Map<String,Object> map = new HashMap<String,Object>();
data.put(“myname”,”李力”);
data.put(“myage”,20);
<select id="selectMultiMap" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student where name=#{myname} or age =#{myage}
</select>
<!--mapper中使用的是#{key}-->
5.6 #和$的区别

#:占位符,告诉 mybatis 使用实际的参数值代替。并使用 PrepareStatement 对象执行 sql 语句, #{}代替sql 语句的“?”。这样做更安全,更迅速,通常也是首选做法

$ : 字符串替换 ,告诉 mybatis 使用包含的“字符串”替换所在位置。使用 Statement 把 sql 语句和${}的内容连接起来。主要用在替换表名,列名,不同列排序等操作。

// 接口方法
Student findByDiffField(@Param("col") String colunName,@Param("cval") Object 
value);
// 这里传入两个参数
// 第一个col代表的是表中的列名,可以通过col动态的选定需要的列名
// 第二个cval代表的是指定列名的列值
<select id="findByDiffField" resultType="com.tbabs.pojo.Student">
 select * from student where ${col} = #{cval}
</select>

6.Mybatis的输出结果resultType

resultType: 执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。resultType 和 resultMap,不能同时使用

6.1简单类型
// 接口方法
int countStudent();
<select id="countStudent" resultType="int">
 select count(*) from student
</select>
<!-- 接收参数为int型,所以resultType="int" -->
6.2对象类型

mybatis框架在处理对象类型底层实现原理

使用构造方法创建对象。调用 setXXX 给属性赋值。

这也说明实体类必须有Getter和Setter

//接口方法
Student selectById(int id);
List<Student> selectByName(String name);
<select id="selectById" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student where id=#{studentId}
</select>
<!--
	这里可以不写parameterType
	因为mybatis可以自己识别参数
-->
<select id="selectByName" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student where id=#{studentId}
</select>
<!--
	这里返回的是一个List,但resultType的内容还是对应List的泛型对象
-->
6.3Map类型

sql 的查询结果作为 Map 的 key 和 value。推荐使用 Map<Object,Object>

注意:Map 作为接口返回值,sql 语句的查询结果最多只能有一条记录。大于一条记录是错误。

//接口方法
Map<Object,Object> selectReturnMap(int id);
<select id="selectReturnMap" resultType="java.util.HashMap">
 select name,email from student where id = #{studentId}
</select>
6.4resultMap

resultMap 可以自定义 sql 的结果和 java 对象属性的映射关系。更灵活的把列值赋值给指定属性。

常用在列名和 java 对象属性名不一样的情况。

使用方式:

1.先定义 resultMap,指定列名和属性的对应关系。

2.在<select>中把 resultType 替换为 resultMap。

//接口方法
List<Student> selectUseResultMap(QueryParam param);
<!--创建 resultMap
 id:自定义的唯一名称,在<select>使用
 type:期望转为的 java 对象的全限定名称或别名 
--> 
<resultMap id="studentMap" type="com.tbabs.pojo.Student">
 <!-- 主键字段使用 id -->
 <id column="id" property="id" />
 <!--非主键字段使用 result -->
 <!-- column 指的是列名, property指的是实体类对应的名字 -->
 <result column="name" property="name"/>
 <result column="email" property="email" />
 <result column="age" property="age" />
</resultMap>

<!--resultMap: resultMap 标签中的 id 属性值--> 
<select id="selectUseResultMap" resultMap="studentMap">
 select id,name,email,age from student where name=#{queryName} or age=#{queryAge}
</select>
6.5实体类的属性名和表的列名不同的处理方式
// 当前实体类
package com.tbabs.pojo;

public class PrimaryStudent {
 private Integer stuId;
 private String stuName;
 private Integer stuAge;
 // set , get 方法
}
// 接口方法
List<PrimaryStudent> selectUseFieldAlias(QueryParam param);
<select id="selectUseFieldAlias" 
resultType="com.bjpowernode.domain.PrimaryStudent">
 select id as stuId, name as stuName,age as stuAge
 from student where name=#{queryName} or age=#{queryAge}
</select>
<!--
	当前表的列名与实体类的属性名不同
	需要在查询时给列名重新赋名字
	使得新的列名与实体类属性相同
-->
6.6模糊查询LIKE

模糊查询的实现有两种方式

一是 java 代码中给查询数据加上%

二是在 mapper 文件 sql 语句的条件位置加上%

// 接口方法
List<Student> selectLike(String name);
<!-- 
	第一种
 	这种在mapper中就是正常的接收参数
	在参数传递时的参数上加上%
	String name = “%XXX%”;
	List<Student> stuList = studentDao.selectLikeFirst(name);
-->
<select id="selectLikeFirst" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student
 where name like #{studentName}
</select>

<!-- 第二种 -->
<select id="selectLikeSecond" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student
 where name like "%" #{studentName} "%"
</select>

7.Mybatis动态SQL

动态 SQL,通过 MyBatis 提供的各种标签对条件作出判断以实现动态拼接 SQL 语句。这里的条件判断使用的表达式为 OGNL 表达式。常用的动态 SQL 标签有<if><where><choose/><foreach>等。MyBatis 的动态 SQL 语句,与 JSTL 中的语句非常相似。

动态 SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的 SQL 语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的 SQL 语句。此时,可使用动态 SQL 来解决这样的问题

在 mapper 的动态 SQL 中若出现大于号(>)、小于号(<)、大于等于号(>=),小于等于号(<=)等符号,最好将其转换为实体符号。否则,XML 可能会出现解析出错问题。

实体符号表

<小于&lt;
>大于&gt;
<=小于等于&lt;=
>=大于等于&gt;=
7.1<if>

对于该标签的执行,当 test 的值为 true 时,会将其包含的 SQL 片断拼接到其所在的 SQL 语句中。

语法:<if test="条件"> sql 语句的部分 </if>

// 接口方法
List<Student> selectStudentIf(Student student);
<select id="selectStudentIf" resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student
 where 1=1
 <if test="name != null and name !='' ">
 and name = #{name}
 </if>
 <if test="age > 0 ">
 and age &gt; #{age}
 </if>
</select>
7.2<where>

<if/>标签的中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1 的子句。因为,若 where 后的所有<if/>条件均为 false,而 where 后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 where,SQL出错。所以,在 where 后,需要添加永为真子句 1=1,以防止这种情况的发生。但当数据量很大时,会严重影响查询效率。

使用<where/>标签,在有查询条件时,可以自动添加上 where 子句;没有查询条件时,不会添加where 子句。需要注意的是,第一个<if/>标签中的 SQL 片断,可以不包含 and。不过,写上 and 也不错,系统会将多出的 and 去掉。但其它<if/>中 SQL 片断的 and,必须要求写上。否则 SQL 语句将拼接出错。

语法:<where> 其他动态 sql </where>

// 接口方法
List<Student> selectStudentWhere(Student student);
<select id="selectStudentWhere" resultType="com.bjpowernode.domain.Student">
 select id,name,email,age from student
 <where>
 <!-- 使用<where>标签不需要在加 1=1 -->
 <if test="name != null and name !='' ">
 and name = #{name}
 </if>
 <if test="age > 0 ">
 and age &gt; #{age}
 </if>
 </where>
</select>
7.3<foreach>

<foreach/>标签用于实现对于数组与集合的遍历。对其使用,需要注意:

➢ collection 表示要遍历的集合类型, list ,array 等。

➢ open、close、separator 为对遍历内容的 SQL 拼接。

语法:

<foreach collection="集合类型" open="开始的字符" close="结束的字符" item="集合中的成员" separator="集合成员之间的分隔符">

    #{item 的值}

</foreach>
7.3.1遍历 List<简单类型>

表达式中的 List 使用 list 表示,其大小使用 list.size 表示。

// 查询学生 id 是 1002,1005,1006
// 封装在idList中
// 接口方法
List<Student> selectStudentForList(List<Integer> idList);
<select id="selectStudentForList" 
resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student
 <if test="list !=null and list.size > 0 ">
 where id in
 <foreach collection="list" open="(" close=")" item="stuid" separator=",">
 #{stuid}
 </foreach>
 </if>
</select>
7.3.2遍历List<对象类型>
// 查询学生 id 是 1002,1005,1006
// 将id封装在student在将student封装在stuList中
// 接口方法
List<Student> selectStudentForList2(List<Student> stuList);
<select id="selectStudentForList2" 
resultType="com.tbabs.pojo.Student">
 select id,name,email,age from student
 <if test="list !=null and list.size > 0 ">
 where id in
 <foreach collection="list" open="(" close=")" item="stuobject" separator=",">
 #{stuobject.id}
 </foreach>
 </if>
</select>
7.4代码片段

<sql/>标签用于定义 SQL 片断,以便其它 SQL 标签复用。而其它标签使用该 SQL 片断,需要使<include/>子标签。该<sql/>标签可以定义 SQL 语句中的任何部分,所以<include/>子标签可以放在动态 SQL的任何位置。

// 接口方法
List<Student> selectStudentSqlFragment(List<Student> stuList);
<!--创建 sql 片段 id:片段的自定义名称-->
<sql id="studentSql">
 select id,name,email,age from student
</sql> 
<select id="selectStudentSqlFragment" resultType="com.tbabs.pojo.Student">
 <!-- 引用 sql 片段 -->
 <include refid="studentSql"/>
 <if test="list !=null and list.size > 0 ">
 where id in
 <foreach collection="list" open="(" close=")" item="stuobject" separator=",">
 #{stuobject.id}
 </foreach>
 </if>
</select>

8.Mybatis配置文件详解

mybatis的配置文件特点:

  1. 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">
    
  2. 根元素 <configuration>

  3. 配置文件内容

    ➢ 定义别名

    ➢ 数据源

    ➢ mapper 文件

mybatis配置文件示例

<?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>
	<!-- 获取db.properties的配置信息 -->
	<properties resource="db.properties"/>
    
    <!-- settings配置,详细请看setting配置部分-->
    <settings>
        <!-- mybatis的日志功能 -->
     	<setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
	
    <!-- 
		定义别名
		定义完成后后续的mapper要标识实体类是只需写实体类的类名即可,无需全称
 	-->
	<typeAliases>
		<package name="com.tbabs.pojo"/>
	</typeAliases>
	
    <!-- 数据库连接 -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
                <!--使用 properties 文件: 语法 ${key}-->
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
        <!-- 这种是一个mapper文件对应一个resource(不常用) -->
		<!--
			<mapper resource="com/tbabs/dao/StudentDao.xml" />
		-->
        <!-- 这种是直接标识包名,在包下找,无需挨个定义(常用)-->
		<package name="com.tbabs.dao"/>
	</mappers>
</configuration>

db.properties(适用于mysql5.7.19版本的,8.0以上的配置略有不同)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/数据库名字
jdbc.username=数据库账户
jdbc.password=数据库密码
8.1dataSource标签

Mybatis 中访问数据库,可以连接池技术,但它采用的是自己的连接池技术。在 Mybatis 的 mybatis.xml

配置文件中,通过<dataSource type="pooled">来实现 Mybatis 中连接池的配置。

dataSource类型

  • UNPOOLED 不使用连接池的数据源

  • POOLED 使用连接池的数据源

  • JNDI 使用 JNDI 实现的数据源

<dataSource type="POOLED">
 <!--连接数据库的四个要素-->
 <property name="driver" value="com.mysql.jdbc.Driver"/>
 <property name="url" value="jdbc:mysql://localhost:3306/数据库名字?charset=utf-8"/>
 <property name="username" value="数据库账户"/>
 <property name="password" value="数据库密码"/>
</dataSource>
<!--
    MyBatis 在初始化时,根据<dataSource>的 type 属性来创建相应类型的的数据源 DataSource,即:
    type=”POOLED”:MyBatis 会创建 PooledDataSource 实例
    type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例
    type=”JNDI”:MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
-->
8.2事务

mybatis默认是需要手动提交事务的

Mybatis 框架是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC 的 Connection对象的 commit(), rollback()

Connection 对象的 setAutoCommit()方法来设置事务提交方式的。分为自动提交和手工提交

<transactionManager type="JDBC"/>

该标签用于指定 MyBatis所使用的事务管理器。MyBatis 支持两种事务管理器类型:JDBC MANAGED

JDBC:使用 JDBC 的事务管理机制。即,通过 Connection 的 commit()方法提交,通过 rollback()方法回滚。但默认情况下,MyBatis 将自动提交功能关闭了,改为了手动提交。即程序中需要显式的对事务进行提交或回滚。从日志的输出信息中可以看到。

MANAGED:由容器来管理事务的整个生命周期(如 Spring 容器)。

mybatis设置事务自动提交的处理方式:

设置自动提交的方式,factory 的 openSession() 分为有参数和无参数的。

SqlSession openSession(); // 无参数的
SqlSession openSession(boolean autoCommit); //有参数的

有参数为 true,使用自动提交,可以修改 MyBatisUtil 的 getSqlSession()方法。

session = factory.openSession(true);

再执行 insert 操作,无需执行 session.commit(),事务是自动提交的

8.3typeAliases(类型别名)

Mybatis 支持默认别名,我们也可以采用自定义别名方式来开发,主要使用在<select resultType="别名">

<!-- 语法 -->
<typeAliases>
 <!--
 定义单个类型的别名
 type:类型的全限定名称
 alias:自定义别名 
 -->
 <typeAlias type="com.tbabs.pojo.Student" alias="mystudent"/>
 <!--
 批量定义别名,扫描整个包下的类,别名为类名(首字母大写或小写都可以)
 name:包名 
 -->
 <package name="com.tbabs.pojo"/>
 <package name="...其他包"/>
</typeAliases>
<!-- mapper.xml 文件,使用别名表示类型 -->
<!-- 无需写全称com.tbabs.pojo.Student -->
<select id="selectStudents" resultType="mystudent(typeAlias)或Student(package)">
 select id,name,email,age from student
</select>
8.4mapper(映射器)
  • <mapper resource=" " />

使用相对于类路径的资源,从 classpath 路径查找文件

例如:<mapper resource="com/tbabs/dao/StudentDao.xml" />

  • <package name=" " />

指定包下的所有 Dao 接口

如:<package name="com.bjpowernode.dao"/>

注意:此种方法要求 Dao 接口名称和 mapper 映射文件名称相同,且在同一个目录中。

8.5settings配置(了解,谨慎配置)
<!-- settings是 MyBatis 中全局的调整设置,它们会改变 MyBatis 的运行时行为,应谨慎设置 -->  
<settings>  
	<!-- 该配置影响的所有映射器中配置的缓存的全局开关。默认值true -->  
	<setting name="cacheEnabled" value="true"/>  
    
	<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认值false  -->  
	<setting name="lazyLoadingEnabled" value="true"/> 
    
	<!-- 是否允许单一语句返回多结果集(需要兼容驱动)。 默认值true -->  
	<setting name="multipleResultSetsEnabled" value="true"/>  
    
	<!-- 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。默认值true -->  
	<setting name="useColumnLabel" value="true"/>  
    
	<!-- 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 默认值false  -->  
	<setting name="useGeneratedKeys" value="false"/> 
    
	<!--  指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 -->   
	<!-- 默认值PARTIAL -->  
	<setting name="autoMappingBehavior" value="PARTIAL"/>  
	<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> 
    
	<!--  配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。默认SIMPLE  -->  
	<setting name="defaultExecutorType" value="SIMPLE"/>  
    
	<!-- 设置超时时间,它决定驱动等待数据库响应的秒数。 -->  
	<setting name="defaultStatementTimeout" value="25"/>  
	<setting name="defaultFetchSize" value="100"/>
    
	<!-- 允许在嵌套语句中使用分页(RowBounds)默认值False -->  
	<setting name="safeRowBoundsEnabled" value="false"/>
    
	<!-- 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。  默认false -->  
	<setting name="mapUnderscoreToCamelCase" value="false"/>  
    
	<!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。  -->  
	<setting name="localCacheScope" value="SESSION"/>  
    
	<!-- 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。  -->  
	<setting name="jdbcTypeForNull" value="OTHER"/>  
    
	<!--   指定哪个对象的方法触发一次延迟加载。  -->  
	<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>  
</settings> 

9.PageHelper插件

Mybatis通用分页插件

github:github.com/pagehelper/…

支持多种数据库:Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL,DB2,SqlServer(2005,2008), Informix,H2,SqlServer2012,Derby,Phoenix

需要添加maven坐标

<dependency>
 <groupId>com.github.pagehelper</groupId>
 <artifactId>pagehelper</artifactId>
 <version>5.1.10</version>
</dependency>

添加到mybatis配置文件中

<!-- 在<environments>之前加入 -->
<plugins>
 <plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>
PageHelper使用

查询语句之前调用 PageHelper.startPage 静态方法。

//page:当前页码 limit:一页显示几条数据
PageHelper.startPage(page,limit);
// 正常查询数据获得全部的信息List
List<Student> list = studentDao.select(Integer id);
// 使用PageInfo获取分页后的数据
PageInfo<Student> pageInfo = new PageInfo<>(list, 5);

PageHelper封装的PageInfo类

public class PageInfo<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    //当前页
    private int pageNum;
    //每页的数量
    private int pageSize;
    //当前页的数量
    private int size;

    //由于startRow和endRow不常用,这里说个具体的用法
    //可以在页面中"显示startRow到endRow 共size条数据"

    //当前页面第一个元素在数据库中的行号
    private int startRow;
    //当前页面最后一个元素在数据库中的行号
    private int endRow;
    //总记录数
    private long total;
    //总页数
    private int pages;
    //结果集
    private List<T> list;

    //前一页
    private int prePage;
    //下一页
    private int nextPage;

    //是否为第一页
    private boolean isFirstPage = false;
    //是否为最后一页
    private boolean isLastPage = false;
    //是否有前一页
    private boolean hasPreviousPage = false;
    //是否有下一页
    private boolean hasNextPage = false;
    //导航页码数
    private int navigatePages;
    //所有导航页号
    private int[] navigatepageNums;
    //导航条上的第一页
    private int navigateFirstPage;
    //导航条上的最后一页
    private int navigateLastPage;

    public PageInfo() {
    }

    /**
     * 包装Page对象
     *
     * @param list
     */
    public PageInfo(List<T> list) {
        this(list, 8);
    }

    /**
     * 包装Page对象
     *
     * @param list          page结果
     * @param navigatePages 页码数量
     */
    public PageInfo(List<T> list, int navigatePages) {
        if (list instanceof Page) {
            Page page = (Page) list;
            this.pageNum = page.getPageNum();
            this.pageSize = page.getPageSize();

            this.pages = page.getPages();
            this.list = page;
            this.size = page.size();
            this.total = page.getTotal();
            //由于结果是>startRow的,所以实际的需要+1
            if (this.size == 0) {
                this.startRow = 0;
                this.endRow = 0;
            } else {
                this.startRow = page.getStartRow() + 1;
                //计算实际的endRow(最后一页的时候特殊)
                this.endRow = this.startRow - 1 + this.size;
            }
        } else if (list instanceof Collection) {
            this.pageNum = 1;
            this.pageSize = list.size();

            this.pages = this.pageSize > 0 ? 1 : 0;
            this.list = list;
            this.size = list.size();
            this.total = list.size();
            this.startRow = 0;
            this.endRow = list.size() > 0 ? list.size() - 1 : 0;
        }
        if (list instanceof Collection) {
            this.navigatePages = navigatePages;
            //计算导航页
            calcNavigatepageNums();
            //计算前后页,第一页,最后一页
            calcPage();
            //判断页面边界
            judgePageBoudary();
        }
    }

    /**
     * 计算导航页
     */
    private void calcNavigatepageNums() {
        //当总页数小于或等于导航页码数时
        if (pages <= navigatePages) {
            navigatepageNums = new int[pages];
            for (int i = 0; i < pages; i++) {
                navigatepageNums[i] = i + 1;
            }
        } else { //当总页数大于导航页码数时
            navigatepageNums = new int[navigatePages];
            int startNum = pageNum - navigatePages / 2;
            int endNum = pageNum + navigatePages / 2;

            if (startNum < 1) {
                startNum = 1;
                //(最前navigatePages页
                for (int i = 0; i < navigatePages; i++) {
                    navigatepageNums[i] = startNum++;
                }
            } else if (endNum > pages) {
                endNum = pages;
                //最后navigatePages页
                for (int i = navigatePages - 1; i >= 0; i--) {
                    navigatepageNums[i] = endNum--;
                }
            } else {
                //所有中间页
                for (int i = 0; i < navigatePages; i++) {
                    navigatepageNums[i] = startNum++;
                }
            }
        }
    }

    /**
     * 计算前后页,第一页,最后一页
     */
    private void calcPage() {
        if (navigatepageNums != null && navigatepageNums.length > 0) {
            navigateFirstPage = navigatepageNums[0];
            navigateLastPage = navigatepageNums[navigatepageNums.length - 1];
            if (pageNum > 1) {
                prePage = pageNum - 1;
            }
            if (pageNum < pages) {
                nextPage = pageNum + 1;
            }
        }
    }

    /**
     * 判定页面边界
     */
    private void judgePageBoudary() {
        isFirstPage = pageNum == 1;
        isLastPage = pageNum == pages || pages == 0;;
        hasPreviousPage = pageNum > 1;
        hasNextPage = pageNum < pages;
    }

10.Mybatis的逆向工程

MyBatis Generator(MBG)是MyBatis MyBatisiBATIS的代码生成器。它将为所有版本的MyBatis以及版本2.2.0之后的iBATIS版本生成代码。它将内省数据库表(或许多表),并将生成可用于访问表的工件。这减少了设置对象和配置文件以与数据库表交互的初始麻烦。MBG寻求对简单CRUD(创建,检索,更新,删除)的大部分数据库操作产生重大影响。您仍然需要为连接查询或存储过程手动编写SQL和对象代码。

使用方法

  • maven插件

    <!-- mybatis逆向工程插件 -->
          <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.5</version>
            <configuration>
              <!--配置文件的位置-->
              <configurationFile>${basedir}/generatorConfig.xml</configurationFile>
              <verbose>true</verbose>
              <overwrite>true</overwrite>
            </configuration>
            <executions>
              <execution>
                <id>Generate MyBatis Artifacts</id>
                <goals>
                  <goal>generate</goal>
                </goals>
              </execution>
            </executions>
            <dependencies>
              <dependency>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-core</artifactId>
                <version>1.3.5</version>
              </dependency>
            </dependencies>
          </plugin>
    
  • MBG配置文件(generatorConfig.xml)

    <?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>
        <!-- 在MBG工作的时候,需要额外加载的依赖包,location属性指明加载jar/zip包的全路径-->
        <!-- 配置mysql 驱动jar包路径 用绝对路径 -->
        <classPathEntry
                location="C:\Environment\LocalRepository\mysql\mysql-connector-java\5.1.9\mysql-connector-java-5.1.9.jar" />
    
        <context id="DB2Tables" targetRuntime="MyBatis3">
    
            <commentGenerator>
                <property name="suppressAllComments" value="true" />
            </commentGenerator>
    
            <!--配置数据库连接信息-->
            <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                            connectionURL="jdbc:mysql://localhost:3306/wyus?useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull"
                            userId="root"
                            password="123456">
            </jdbcConnection>
    
            <!-- java类型处理器 
            用于处理DB中的类型到Java中的类型,默认使用JavaTypeResolverDefaultImpl;
            注意一点,默认会先尝试使用Integer,Long,Short等来对应DECIMAL和 NUMERIC数据类型; 
        	-->
            <javaTypeResolver >
                <property name="forceBigDecimals" value="false" />
            </javaTypeResolver>
    
            <!-- 实体类生成策略 -->
            <javaModelGenerator targetPackage="com.tbabs.pojo" targetProject=".\src\main\java">
                <property name="enableSubPackages" value="true" />
                <property name="trimStrings" value="true" />
            </javaModelGenerator>
    
            <!-- mapper.xml生成策略 -->
            <sqlMapGenerator targetPackage="mapper"  targetProject=".\src\main\resources">
                <property name="enableSubPackages" value="true" />
            </sqlMapGenerator>
    
            <!-- dao接口生成策略 -->
            <javaClientGenerator type="XMLMAPPER" targetPackage="com.tbabs.dao"  targetProject=".\src\main\java">
                <property name="enableSubPackages" value="true" />
            </javaClientGenerator>
    
    		<!-- 指定哪个表的生成策略,生成对应的表(逆向分析表) -->
    		<table tableName="indent" domainObjectName="Order"/>
            <table tableName="achievement" />
        </context>
    </generatorConfiguration>
    
  • 操作

    进入idea的maven面板-->plugings-->mybatis-generator-->双击构建即可

逆向工程生成的mapper接口解析

mapper接口中的方法

方法功能说明
long countByExample(EmployeeExample example) ;按条件计数
int deleteByExample(EmployeeExample example);按条件删除
int deleteByPrimaryKey(Integer id);按主键删除
int insert(Employee record);插入数据
int insertSelective(Employee record);按条件插入数据
List selectByExample(EmployeeExample example);按条件查询
Employee selectByPrimaryKey(Integer id);按主键查询
int updateByExampleSelective(@Param(“record”) Employee record, @Param(“example”) EmployeeExample example);按条件更新值不为null的字段
int updateByExample(@Param(“record”) Employee record, @Param(“example”) EmployeeExample example);按条件更新
int updateByPrimaryKeySelective(Employee record);按主键更新值不为null的字段
int updateByPrimaryKey(Employee record);按主键更新

Example实例

方法说明
example.setOrderByClause(“字段名 ASC”)添加升序排列条件,DESC为降序
example.setDistinct(false)去除重复,boolean型,true为选择不重复的记录
criteria.andXxxIsNull添加字段xxx为null的条件
criteria.andXxxIsNotNull添加字段xxx不为null的条件
criteria.andXxxEqualTo(value)添加xxx字段等于value条件
criteria.andXxxNotEqualTo(value)添加xxx字段不等于value条件
criteria.andXxxGreaterThan(value)添加xxx字段大于value条件
criteria.andXxxGreaterThanOrEqualTo(value)添加xxx字段大于等于value条件
criteria.andXxxLessThan(value)添加xxx字段小于value条件
criteria.andXxxLessThanOrEqualTo(value)添加xxx字段小于等于value条件
criteria.andXxxIn(List<?>)添加xxx字段值在List<?>条件
criteria.andXxxNotIn(List<?>)添加xxx字段值不在List<?>条件
criteria.andXxxLike(“%”+value+”%”)添加xxx字段值为value的模糊查询条件
criteria.andXxxNotLike(“%”+value+”%”)添加xxx字段值不为value的模糊查询条件
criteria.andXxxBetween(value1,value2)添加xxx字段值在value1和value2之间条件
criteria.andXxxNotBetween(value1,value2)添加xxx字段值不在value1和value2之间条件

11.附录

Mybatis默认支持的别名

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
mapMap/HashMap