Mybatis简介:

  • Mybatis过程:
    Mybatis - 图1
    Mybatis - 图2
    Mybatis - 图3

Mybatis入门案例:

案例实现:

  • 导入依赖(Mybatis和日志)
    1. <dependency>
    2. <groupId>junit</groupId>
    3. <artifactId>junit</artifactId>
    4. <version>4.13.1</version>
    5. <scope>compile</scope>
    6. </dependency>
    7. <dependency>
    8. <groupId>log4j</groupId>
    9. <artifactId>log4j</artifactId>
    10. <version>1.2.17</version>
    11. </dependency>
    12. <dependency>
    13. <groupId>org.slf4j</groupId>
    14. <artifactId>slf4j-log4j12</artifactId>
    15. <version>1.6.6</version>
    16. <scope>test</scope>
    17. </dependency>


使用日志需要配置log4j.properties

  1. ######################## 将等级为DEBUG的日志信息输出到consoleh和file两个目的地, console和file的定义在下面的代码
  2. log4j.rootLogger=DEBUG,console,file
  3. ########################控制台输出的相关设置
  4. log4j.appender.console = org.apache.log4j.ConsoleAppender
  5. #在控制台输出
  6. log4j.appender.console.Target = System.out
  7. #在DEBUG级别输出
  8. log4j.appender.console.Threshold = DEBUG
  9. log4j.appender.console.layout = org.apache.log4j.PatternLayout
  10. #日志格式
  11. log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
  12. ######################日志输出级别
  13. log4j.logger.org.mybatis=DEBUG
  14. log4j.logger.java.sql=DEBUG
  15. log4j.logger.java.sql.Statement=DEBUG
  16. log4j.logger.java.sql.ResultSet=DEBUG
  17. log4j.logger.java.sql.PreparedStatement=DEBUG
  • 在数据库中创建表并创建实体类(字段名需一致,不一致则需要取别名操作)
    Mybatis - 图4 ```java package bean;

public class Book { private String userId; private String username; private String ustatus;

  1. public Book(String userId, String username, String ustatus) {
  2. this.userId = userId;
  3. this.username = username;
  4. this.ustatus = ustatus;
  5. }
  6. public Book() {
  7. }
  8. public String getUserId() {
  9. return userId;
  10. }
  11. public void setUserId(String userId) {
  12. this.userId = userId;
  13. }
  14. public String getUsername() {
  15. return username;
  16. }
  17. public void setUsername(String username) {
  18. this.username = username;
  19. }
  20. public String getUstatus() {
  21. return ustatus;
  22. }
  23. public void setUstatus(String ustatus) {
  24. this.ustatus = ustatus;
  25. }
  26. @Override
  27. public String toString() {
  28. return "Book{" +
  29. "userId='" + userId + '\'' +
  30. ", username='" + username + '\'' +
  31. ", ustatus='" + ustatus + '\'' +
  32. '}';
  33. }

}

  1. -
  2. 配置Mybatis全局配置文件
  3. ```xml
  4. <?xml version="1.0" encoding="UTF-8" ?>
  5. <!DOCTYPE configuration
  6. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  7. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  8. <configuration>
  9. <environments default="development">
  10. <environment id="development">
  11. <transactionManager type="JDBC"/>
  12. <dataSource type="POOLED">
  13. <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
  14. <property name="url" value="jdbc:mysql://localhost:3306/user"/>
  15. <property name="username" value="root"/>
  16. <property name="password" value="LJLljl20020728.+"/>
  17. </dataSource>
  18. </environment>
  19. </environments>
  20. <mappers>
  21. <mapper resource="BookMapper.xml"/>
  22. </mappers>
  23. </configuration>
  • 配置sql映射文件
    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="org.mybatis.example.BlogMapper">
    6. <!--
    7. namespace:名称空间
    8. id:唯一标识
    9. resultType:返回值类型
    10. #{id}:从传递过来的参数中取出id值
    11. -->
    12. <select id="selectBook" resultType="bean.Book">
    13. select * from book where userId = #{id}
    14. </select>
    15. </mapper>
  • 编写测试类 ```java package mybatistest;

import bean.Book; 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 java.io.IOException; import java.io.InputStream;

public class test { /**

  1. * 1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
  2. * 2.sql映射文件:配置每一个sql,以及sql的封装规则等
  3. * 3.sql映射文件注册在全局配置文件中
  4. * 4.写代码
  5. * 1 根据全局配置文件得到SqlSessionFactory
  6. * 2 使用sqlSession工厂,获取到sqlSession对象使用他来执行增删改查
  7. * 一个sqlSession就是代表和数据库的依次绘画,用完关闭
  8. * 3 使用sql的唯一标志老高斯Mybatis执行那个sql,而sql都是保存在映射文件中的 BookMapper.xml
  9. * @throws IOException
  10. */
  11. @Test
  12. public void MybatisTest() throws IOException {
  13. //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
  14. String resource = "Mybatis-Config.xml";
  15. InputStream inputStream = Resources.getResourceAsStream(resource);
  16. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  17. //2.创建SqlSession实例,能直接执行已经映射的sql语句
  18. SqlSession sqlSession = sqlSessionFactory.openSession();
  19. /**
  20. * selectOne中有2个参数:
  21. * 第一个参数为sql唯一标识
  22. * 第二个为执行sql要使用的参数
  23. */
  24. try {
  25. Book selectBook = sqlSession.selectOne("selectBook", 1);
  26. System.out.println(selectBook);
  27. } finally {
  28. sqlSession.close();
  29. }
  30. }

}

  1. <a name="fd89c2ec"></a>
  2. ## 面向接口编程:
  3. -
  4. 创建映射接口
  5. ```java
  6. package mybatistest;
  7. import bean.Book;
  8. public interface BookMapper {
  9. public Book getBookOne(String userId);
  10. }
  • 修改命名空间为接口的全类名,和映射接口对应
    1. <mapper namespace="mybatistest.BookMapper">
  • id为实现接口的方法
    1. <select id="getBookOne" resultType="bean.Book">
    2. select * from book where userId = #{id}
    3. </select>
  • 测试类:

    1. package mybatistest;
    2. import bean.Book;
    3. import org.apache.ibatis.io.Resources;
    4. import org.apache.ibatis.session.SqlSession;
    5. import org.apache.ibatis.session.SqlSessionFactory;
    6. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    7. import org.junit.Test;
    8. import java.io.IOException;
    9. import java.io.InputStream;
    10. public class test {
    11. @Test
    12. public void MybatisTest() throws IOException {
    13. //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
    14. String resource = "Mybatis-Config.xml";
    15. InputStream inputStream = Resources.getResourceAsStream(resource);
    16. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    17. //2.创建SqlSession实例,能直接执行已经映射的sql语句
    18. SqlSession sqlSession = sqlSessionFactory.openSession();
    19. try {
    20. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
    21. Book book = mapper.getBookOne("1");
    22. System.out.println(book);
    23. } finally {
    24. sqlSession.close();
    25. }
    26. }
    27. }

总结:

  • 接口式编程
    原生 -> Dao -> DaoImpl
    mybatis -> Mapper ->xxMapper.xml

  • SqlSession代表和数据库一次会话:用完必须关闭

  • SqlSession和connection一样都是非线程安全。每次使用都应该去获取新的对象

  • mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。(将接口和xml进行绑定)

    1. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
  • 两个重要的配置文件:

    • mybatis的全局变量配置文件:包含数据库连接池信息,事务管理器操作等…系统运行环境信息。
    • sql映射文件:保存了每一个sql语句的映射信息,将sql去出来。

全局配置文件:

  • 全局配置文件configuration标签的配置顺序:
    ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?,
    objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?)

properties—引入外部配置文件:

  • jdbc.properties 编写连接池信息
    1. pro.driverClass=com.mysql.jdbc.Driver
    2. pro.url=jdbc:mysql://localhost:3306/user
    3. pro.username=root
    4. pro.password=LJLljl20020728.+
  • 引入外部配置文件,并替换其值
    1. <configuration>
    2. <properties resource="jdbc.properties"></properties>
    3. <environments default="development">
    4. <environment id="development">
    5. <transactionManager type="JDBC"/>
    6. <dataSource type="POOLED">
    7. <property name="driver" value="${pro.driverClass}"></property>
    8. <property name="url" value="${pro.url}"></property>
    9. <property name="username" value="${pro.username}"></property>
    10. <property name="password" value="${pro.password}"></property>
    11. </dataSource>
    12. </environment>
    13. </environments>
    14. <mappers>
    15. <mapper resource="BookMapper.xml"/>
    16. </mappers>
    17. </configuration>

setting—运行时行为设置:

  • settings包含很多重要设置项

    • setting用来设置每一个设置项

      • name:设置项名
      • value:设置项取值

Mybatis - 图5

typeAliases—别名

  • typeAlias:为某个java类型取别名

    • type:指定要起别名的类型全类名;默认别名就是类名小写
    • alias:指定新的别名

      1. <typeAliases>
      2. <typeAlias type="bean.Book" alias="book"></typeAlias>
      3. </typeAliases>
    • 在xxxmapper.xml中可以直接使用

      1. <select id="getBookOne" resultType="book">
  • package:为某个包下的所有批量取别名

    • name:指定包名(为当前包以及下面所有后代包的每一个类都起一个默认别名(类名小写))
      1. <typeAliases>
      2. <!-- <typeAlias type="bean.Book" alias="book"></typeAlias>-->
      3. <package name="bean"/>
      4. </typeAliases>
  • 批量取别名的情况下,使用@Alias注解为某个类型指定新的别名
    1. @Alias(value = "book")
    2. public class Book

plugings—插件

  • MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

    • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

    • ParameterHandler (getParameterObject, setParameters)

    • ResultSetHandler (handleResultSets, handleOutputParameters)

    • StatementHandler (prepare, parameterize, batch, update, query)

    • 这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。
      通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

environments—环境配置

  • MyBatis 可以配置成适应多种环境,default指定使用某种环境(将default值设为那个环境的id值),可以达到快速切换。例如,开发、测试和生产环境需要有不同的配置。尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

    • 默认使用的环境 ID(比如:default=”development”)。

    • 每个 environment 元素定义的环境 ID(比如:id=”development”)。

    • 事务管理器的配置(比如:type=”JDBC”)。

    • 数据源的配置(比如:type=”POOLED”)。

      1. <environments default="development">
      2. <environment id="development">
      3. <transactionManager type="JDBC"/>
      4. <dataSource type="POOLED">
      5. <property name="driver" value="${pro.driverClass}"></property>
      6. <property name="url" value="${pro.url}"></property>
      7. <property name="username" value="${pro.username}"></property>
      8. <property name="password" value="${pro.password}"></property>
      9. </dataSource>
      10. </environment>
      11. <environment id="development">
      12. ...
      13. </environment>
      14. </environments>
  • 事务管理器(transactionManager):在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”)type为指定全类名

    • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
    • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
    • 自定义事务管理器: TransactionFactory 接口实现类的全限定名或类型别名代替它们。在事务管理器实例化后,所有在 XML 中配置的属性将会被传递给 setProperties() 方法。你的实现还需要创建一个 Transaction 接口的实现类。使用这两个接口,你可以完全自定义 MyBatis 对事务的处理。
  • 数据源(dataSource):元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

    • UNPOOLED-这个数据源的实现会每次请求时打开和关闭连接。

    • POOLED– 这种数据源的实现利用“”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

    • JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

    • 自定义数据源:你可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory 来使用第三方数据源实现:

      1. public interface DataSourceFactory {
      2. void setProperties(Properties props);
      3. DataSource getDataSource();
      4. }


以C3P0为例:

  1. import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
  2. import com.mchange.v2.c3p0.ComboPooledDataSource;
  3. public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
  4. public C3P0DataSourceFactory() {
  5. this.dataSource = new ComboPooledDataSource();
  6. }
  7. }
  1. <dataSource type="org.myproject.C3P0DataSourceFactory">
  2. <property name="driver" value="org.postgresql.Driver"/>
  3. <property name="url" value="jdbc:postgresql:mydb"/>
  4. <property name="username" value="postgres"/>
  5. <property name="password" value="root"/>
  6. </dataSource>

databaseIdProvider—数据库厂商标识

  • MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。
    1. <databaseIdProvider type="DB_VENDOR">
    2. <property name="SQL Server" value="sqlserver"/>
    3. <property name="DB2" value="db2"/>
    4. <property name="Oracle" value="oracle" />
    5. </databaseIdProvider>
  1. <select id="getBookOne" resultType="book" databaseId="db2">
  2. select * from book where userId = #{id}
  3. </select>
  4. <select id="getBookOne" resultType="book" databaseId="sqlserver">
  5. select * from book where userId = #{id}
  6. </select>
  1. 切换环境则可以对不同的数据库执行标识的操作。

Mappers—映射器

  • 既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。

      1. <!-- 使用相对于类路径的资源引用 -->
      2. <mappers>
      3. <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
      4. <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
      5. <mapper resource="org/mybatis/builder/PostMapper.xml"/>
      6. </mappers>
    1. <!-- 使用完全限定资源定位符(URL) -->
    2. <mappers>
    3. <mapper url="file:///var/mappers/AuthorMapper.xml"/>
    4. <mapper url="file:///var/mappers/BlogMapper.xml"/>
    5. <mapper url="file:///var/mappers/PostMapper.xml"/>
    6. </mappers>
  • class:引用(注册)接口

    • 有sql映射文件,映射名必须和接口一致,并且放在同一目录下

    • 没有sql映射文件,所有的sql都是利用注解紫萼在接口上

    • 推荐:比较重要的,复杂的Dao接口我们来写SQL映射文件,不重要,简单的Dao接口为了快速开发可以使用注解
      接口方法中写需要操作对应的注释

      1. public interface BookMapper {
      2. @Select("select * from book where userId = #{id}")
      3. public Book getBookOne(String userId);
      4. }


配置相应的mapper

  1. <mappers>
  2. <mapper class="mybatistest.BookMapper"></mapper>
  3. </mappers>
  • 批量注册:(情况和class相似,分两种情况)
    1. <!-- 将包内的映射器接口实现全部注册为映射器 -->
    2. <mappers>
    3. <package name="org.mybatis.builder"/>
    4. </mappers>

XML映射文件:

增删改操作:

  • mybatis允许增删改直接定义Integer,Long,Boolean类型返回值,mybatis会自动封装结果

  • 需要手动提交 sqlSessionFactory.openSession() 不需要手动提交 sqlSessionFactory.openSession(true)

  • | 属性 | 描述 | | :—- | :—- | | id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 | | parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 | | parameterMap | 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 | | flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 | | timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 | | statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 | | useGeneratedKeys | (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 | | keyProperty | (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。 | | keyColumn | (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 | | databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |

  • 实例:

    • 映射接口
      1. public interface BookMapper {
      2. // @Select("select * from book where userId = #{id}")
      3. public Book getBookOne(String userId);
      4. public void updateBook(Book book);
      5. public void deleteBook(String userId);
      6. public void addBook(Book book);
      7. }
  • 映射文件
    1. <mapper namespace="mybatistest.BookMapper">
    2. <!--
    3. namespace:名称空间
    4. id:唯一标识
    5. resultType:返回值类型
    6. #{id}:从传递过来的参数中取出id值
    7. -->
    8. <select id="getBookOne" resultType="book">
    9. select * from book where userId = #{id}
    10. </select>
    11. <update id="updateBook" >
    12. update book set userId=#{userId},username=#{username},ustatus=#{ustatus} where userId=#{userId}
    13. </update>
    14. <insert id="addBook">
    15. insert into book (userId,username,ustatus) values (#{userId},#{username},#{ustatus})
    16. </insert>
    17. <delete id="deleteBook">
    18. delete from book where userId=#{id}
    19. </delete>
    20. </mapper>
  • 测试类
    1. @Test
    2. public void test1() throws IOException {
    3. String resource = "Mybatis-Config.xml";
    4. InputStream inputStream = Resources.getResourceAsStream(resource);
    5. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    6. SqlSession sqlSession = sqlSessionFactory.openSession();
    7. try {
    8. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
    9. //添加
    10. mapper.addBook(new Book("3","C#","5"));
    11. //修改
    12. mapper.updateBook(new Book("3","Python","1"));
    13. //删除
    14. mapper.deleteBook("3");
    15. sqlSession.commit();
    16. } finally {
    17. sqlSession.close();
    18. }
    19. }

insert获取自增主键的值:

  • mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys()

    • useGeneratedKeys=“true”:使用自增获取主键值的策略
    • keyProperty:指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的那个属性
  • 测试:

      1. <insert id="addBook" useGeneratedKeys="true" keyProperty="userId">
      2. insert into book (userId,username,ustatus) values (#{userId},#{username},#{ustatus})
      3. </insert>
    1. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
    2. //添加
    3. Book book = new Book("3", "C#", "5");
    4. mapper.addBook(book);

批量插入:

  • 获取sqlSession时传入值,可以单独让mysql对着次会话进行批量插入
  1. SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
  • 与spring整合时:

    • 配置文件编写:

      1. <!--配置批量执行的sqlSession-->
      2. <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
      3. <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
      4. <constructor-arg name="executorType" value="BATCH"></constructor-arg>
      5. </bean>
    • 在Service中自动注入则可以使用批量:

      1. @Autowired
      2. private SqlSession sqlSessiom;

参数:

单个参数:

  • mybatis不会做特殊处理

  • {参数名}:取出参数值

多个参数:

  • 多个参数会被封装为一个map

    • key:param1…paramN,或者参数的索引也可以

    • value:传入的参数值

    • {}就是从map中获取指定的key值

    • 测试:

      1. <select id="getBookOne" resultType="book">
      2. select * from book where userId = #{param1} and username=#{param2}
      3. </select>
  • 命名参数:确定指定封装参数时map的key:@Param(“id”),多个参数会被封装为一个map

    • key:使用@Param注解指定的值

    • value:参数值

    • {指定的key}取出对应的参数值

    • 测试:

      1. public Book getBookOne(@Param("userId") String userId, @Param("username") String username);
  1. <select id="getBookOne" resultType="book">
  2. select * from book where userId = #{userId} and username=#{username}
  3. </select>

POJO:

  • 如果多个参数正好是我们业务逻辑的模型,我们可以直接,传入pojo

    • {属性名},取出传入的pojo的属性值

    1. public void updateBook(Book book);

Map:

  • 如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map

    • {key}:取出map中对应的值

    1. public Book getBookOne(Map<String,String> map);
  1. Map<String,String> map=new HashMap<>();
  2. map.put("userId","1");
  3. map.put("username","java");
  4. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
  5. Book book = mapper.getBookOne(map);
  6. System.out.println(book);
  1. <select id="getBookOne" resultType="book">
  2. select * from book where userId = #{userId} and username=#{username}
  3. </select>

TO:

  • 如果多个参数不是业务模型中的数据,但是经常使用,推荐编写一个TO(Transfer Object)数据传输对象

    • 如分页操作:
      Page{
      int index;
      int size;
      }

Collection:

  • 如果是Collection(List,Set)类型或者是数组,也会特殊处理,也就是把传入的list或者数组封装在map中

    • key:Collection(collection),如果是List还可以使用(List),数组(array)
  • public Book getBookById(List userId)
    取值:取出第一个id的值:#{list[0]}

参数封装map的过程:(源码解析)

  • names:{0=id, 1=lastName};构造器的时候就确定好了

    1. 确定流程:<br />
    2. 1.获取每个标了param注解的参数的@Param的值:idlastName 赋值给name;<br />
    3. 2.每次解析一个参数给map中保存信息:(key:参数索引,valuename的值)<br />
    4. name的值:<br />
    5. 标注了param注解:注解的值<br />
    6. 没有标注:<br />
    7. 1.全局配置:useActualParamNamejdk1.8):name=参数名<br />
    8. 2.name=map.size();相当于当前元素的索引<br />
    9. {0=id, 1=lastName,2=2}
    10. args1"Tom",'hello'】:
  1. public Object getNamedParams(Object[] args) {
  2. final int paramCount = names.size();
  3. //1、参数为null直接返回
  4. if (args == null || paramCount == 0) {
  5. return null;
  6. //2、如果只有一个元素,并且没有Param注解;args[0]:单个参数直接返回
  7. } else if (!hasParamAnnotation && paramCount == 1) {
  8. return args[names.firstKey()];
  9. //3、多个元素或者有Param标注
  10. } else {
  11. final Map<String, Object> param = new ParamMap<Object>();
  12. int i = 0;
  13. //4、遍历names集合;{0=id, 1=lastName,2=2}
  14. for (Map.Entry<Integer, String> entry : names.entrySet()) {
  15. //names集合的value作为key; names集合的key又作为取值的参考args[0]:args【1,"Tom"】:
  16. //eg:{id=args[0]:1,lastName=args[1]:Tom,2=args[2]}
  17. param.put(entry.getValue(), args[entry.getKey()]);
  18. // add generic param names (param1, param2, ...)param
  19. //额外的将每一个参数也保存到map中,使用新的key:param1...paramN
  20. //效果:有Param注解可以#{指定的key},或者#{param1}
  21. final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
  22. // ensure not to overwrite parameter named with @Param
  23. if (!names.containsValue(genericParamName)) {
  24. param.put(genericParamName, args[entry.getKey()]);
  25. }
  26. i++;
  27. }
  28. return param;
  29. }
  30. }
  31. }

总结:参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key;
#{key}就可以取出map中的值;

参数值获取:

  • 和$的区别:

    • 实例:
      select from tbl_employee where id=${id} and last_name=#{lastName}
      运行进去的sql语句: Preparing: select
      from tbl_employee where id=2 and last_name=?

    • {}:可以获取map中的值或者pojo对象属性的值,#{}是以预编译的形式,将参数设置到sql语句中;使用PreparedStatement;防止sql注入问题,大多情况下,我们去参数的值都应该去使用#{};

    • Mybatis - 图6{}是取出的值直接拼装在sql语句中;会有安全问题;

    • 原生jdbc不支持占位符的地方我们就可以使用${}进行取值, 比如分表、排序,按照年份分表拆分:
      select from ${year}_salary where xxx;
      select
      from tbl_employee order by ${f_name} ${order}

    • 补充:sql注入问题
      所谓SQL注入(sql inject),就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。

  • {}更丰富的用法:

    • 规定参数的一些规则:

      • javaType、 jdbcType、 mode(存储过程)、 numericScale、resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);

      • JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;
        由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法
        1、#{email,jdbcType=OTHER};
        2、jdbcTypeForNull=NULL

查询:

select返回list集合

  • 测试:

    • 接口中写方法
      1. public List<Book> getBooks();
  • 映射文件中编写select语句
    1. <!--如果返回的是一个集合,resultType写集合中元素的类型-->
    2. <select id="getBooks" resultType="book">
    3. select * from book
    4. </select>
  • 测试方法:
    1. @Test
    2. public void MybatisTest() throws IOException {
    3. //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
    4. String resource = "Mybatis-Config.xml";
    5. InputStream inputStream = Resources.getResourceAsStream(resource);
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    7. //2.创建SqlSession实例,能直接执行已经映射的sql语句
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. /**
    10. * selectOne中有2个参数:
    11. * 第一个参数为sql唯一标识
    12. * 第二个为执行sql要使用的参数
    13. */
    14. try {
    15. //查询所有对象
    16. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
    17. List<Book> books = mapper.getBooks();
    18. for(Book book:books){
    19. System.out.println(book);
    20. }
    21. } finally {
    22. sqlSession.close();
    23. }
    24. }

select返回map集合:

  • 测试:

    • 接口写方法 ```java //查询单个对象返回map public Map getBooksForMap(String userId);

//查询多个对象返回map //多条记录封装在一个map,Map中的key是这条记录的主键,值是记录封装后的javaBean对象 //告诉mybatis封装这个map的时候使用哪个属性作为map的key @MapKey(“userId”) public Map getAllBookForMap();

  1. -
  2. 映射文件中编写select语句
  3. ```xml
  4. <select id="getBooksForMap" resultType="Map">
  5. select * from book where userId = #{userId}
  6. </select>
  7. <select id="getAllBookForMap" resultType="Map">
  8. select * from book
  9. </select>
  • 测试方法:

    1. @Test
    2. public void MybatisTest() throws IOException {
    3. //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
    4. String resource = "Mybatis-Config.xml";
    5. InputStream inputStream = Resources.getResourceAsStream(resource);
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    7. //2.创建SqlSession实例,能直接执行已经映射的sql语句
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. /**
    10. * selectOne中有2个参数:
    11. * 第一个参数为sql唯一标识
    12. * 第二个为执行sql要使用的参数
    13. */
    14. try {
    15. //查询对象已map返回
    16. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
    17. Map<String, String> booksForMap = mapper.getBooksForMap("1");
    18. System.out.println(booksForMap);
    19. Map<String, Object> allBookForMap = mapper.getAllBookForMap();
    20. System.out.println(allBookForMap);
    21. } finally {
    22. sqlSession.close();
    23. }
    24. }

resultMap自定义结果集映射规则:

  • 测试:(

    • 接口写方法:
      1. //自定义映射结果查询
      2. public Book getBookById(String userId);
  • 映射文件中编写select语句:
    1. <resultMap id="myBook" type="book">
    2. <!--指定的主键列的封装规则:
    3. id:定义主键会底层优化
    4. column:指定哪一列
    5. property:指定对应的javaBean属性
    6. -->
    7. <id column="userid" property="userId"></id>
    8. <!--定义普通列的封装规则
    9. 其他不指定的列会自动封装,我们只要写resultMap把全部的映射规则都写上 -->
    10. <result column="username" property="username"></result>
    11. <result column="ustatus" property="ustatus"></result>
    12. </resultMap>
    13. <!--resultMap:自定义结果集映射规则-->
    14. <select id="getBookById" resultType="book">
    15. select *from book where userid=#{userId}
    16. </select>
  • 测试方法:
    1. @Test
    2. public void MybatisTest() throws IOException {
    3. //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
    4. String resource = "Mybatis-Config.xml";
    5. InputStream inputStream = Resources.getResourceAsStream(resource);
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    7. //2.创建SqlSession实例,能直接执行已经映射的sql语句
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. /**
    10. * selectOne中有2个参数:
    11. * 第一个参数为sql唯一标识
    12. * 第二个为执行sql要使用的参数
    13. */
    14. try {
    15. //查询返回自定义对象
    16. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
    17. Book bookById = mapper.getBookById("1");
    18. System.out.println(bookById);
    19. } finally {
    20. sqlSession.close();
    21. }
    22. }
  • 常用方法,查询的字段和javaBean中的字段不匹配需要经常使用。

关联查询:

级联属性封装结果:

  • 数据库中创建两张表:
    Mybatis - 图7
    Mybatis - 图8

  • 接口写方法:

    1. //关联查询
    2. public Book getBookAndEditionById(String userId);
  • Book类中加入edition属性值: ```java package bean;

import org.apache.ibatis.type.Alias;

@Alias(value = “book”) public class Book { private String userId; private String username; private String ustatus; private Edition edition;

  1. public Book(String userId, String username, String ustatus, Edition edition) {
  2. this.userId = userId;
  3. this.username = username;
  4. this.ustatus = ustatus;
  5. this.edition = edition;
  6. }
  7. public Edition getEdition() {
  8. return edition;
  9. }
  10. public void setEdition(Edition edition) {
  11. this.edition = edition;
  12. }
  13. public Book() {
  14. }
  15. public String getUserId() {
  16. return userId;
  17. }
  18. public void setUserId(String userId) {
  19. this.userId = userId;
  20. }
  21. public String getUsername() {
  22. return username;
  23. }
  24. public void setUsername(String username) {
  25. this.username = username;
  26. }
  27. public String getUstatus() {
  28. return ustatus;
  29. }
  30. public void setUstatus(String ustatus) {
  31. this.ustatus = ustatus;
  32. }
  33. @Override
  34. public String toString() {
  35. return "Book{" +
  36. "userId='" + userId + '\'' +
  37. ", username='" + username + '\'' +
  38. ", ustatus='" + ustatus + '\'' +
  39. ", edition=" + edition +
  40. '}';
  41. }

}

  1. -
  2. 映射文件中编写select语句:
  3. <br />此时使用的是inner join on 内连接查询,也可以设定主键进行查询,也可以使用左连接查询
  4. ```xml
  5. <resultMap id="myBookAndEdition" type="book">
  6. <id column="userId" property="userId"></id>
  7. <result column="username" property="username"></result>
  8. <result column="ustatus" property="ustatus"></result>
  9. <!--联合查询:级联属性封装结果集-->
  10. <result column="editId" property="edition.editId"></result>
  11. <result column="editName" property="edition.editName"></result>
  12. </resultMap>
  13. <select id="getBookAndEditionById" resultMap="myBookAndEdition">
  14. select * from book b inner join edition e on b.deitId=e.editId where b.userid=#{userId};
  15. </select>
  • 测试:
    1. @Test
    2. public void MybatisTest() throws IOException {
    3. //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
    4. String resource = "Mybatis-Config.xml";
    5. InputStream inputStream = Resources.getResourceAsStream(resource);
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    7. //2.创建SqlSession实例,能直接执行已经映射的sql语句
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. /**
    10. * selectOne中有2个参数:
    11. * 第一个参数为sql唯一标识
    12. * 第二个为执行sql要使用的参数
    13. */
    14. try {
    15. //查询返回自定义对象
    16. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
    17. Book bookAndEditionById = mapper.getBookAndEditionById("1");
    18. System.out.println(bookAndEditionById);
    19. } finally {
    20. sqlSession.close();
    21. }
    22. }

association分步查询:

  • 方法一:将select标签进行替换:
    1. <!--使用association定义关联的单个对象的封装规则:-->
    2. <resultMap id="myBookAndEdition" type="book">
    3. <id column="userId" property="userId"></id>
    4. <result column="username" property="username"></result>
    5. <result column="ustatus" property="ustatus"></result>
    6. <!--association可以指定联合的javaBean对象
    7. property="edition" ,指定哪个属性是联合的对象
    8. javaType:指定这个属性对象的类型[不能省略]
    9. -->
    10. <association property="edition" javaType="edition">
    11. <id column="editId" property="editId"></id>
    12. <result column="editName" property="editName"></result>
    13. </association>
    14. </resultMap>
    15. <select id="getBookAndEditionById" resultMap="myBookAndEdition">
    16. select * from book b inner join edition e on b.deitId=e.editId where b.userid=#{userId};
    17. </select>
  • 方法二:创建查找edition的方法进行分布查询

    • 创建接口方法:
      1. public interface EditionMapper {
      2. public Edition getEditionById(String editId);
      3. }
  • 映射文件:
    1. <mapper namespace="mybatistest.EditionMapper">
    2. <select id="getEditionById" resultType="edition">
    3. select * from edition where editId=#{editId}
    4. </select>
    5. </mapper>
  • Book的select标签:
    1. <resultMap id="myBookAndEdition" type="book">
    2. <id column="userId" property="userId"></id>
    3. <result column="username" property="username"></result>
    4. <result column="ustatus" property="ustatus"></result>
    5. <!--association定义关联对象的封装规则
    6. select:表明当前属性是调用select指定的方法查出的结果
    7. column:指定将哪一列的值传给这个方法
    8. 流程:使用select指定的方法(传入column指定的这列的参数的值)查出对象,并封装给property指定的属性
    9. -->
    10. <association property="edition" select="mybatistest.EditionMapper.getEditionById" column="editId">
    11. </association>
    12. </resultMap>
    13. <select id="getBookAndEditionById" resultMap="myBookAndEdition">
    14. select * from book where userId=#{userId}
    15. </select>
  • edition的select标签:
    1. <select id="getEditionById" resultType="edition">
    2. select * from edition where editId=#{editId}
    3. </select>
  • 延时加载:

    • 可以使用延时加载(懒加载):(按需加载)
      我们每次查询Book对象的时候,都会将edition一起查询
      延时加载可以使出版社信息在我们使用的时候再去查询
      分段查询的基础上加上两个配置

    • 在全局配置文件中配置懒加载: | lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false | | —- | —- | —- | —- | | aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 | true | false | false (在 3.4.1 及之前的版本中默认为 true) |

  1. <settings>
  2. <setting name="lazyLoadingEnabled" value="true"/>
  3. </settings>


新版本mybatis已经把aggressiveLazyLoading设置为false,不需要手动设置

  • 测试:
    1. Book bookAndEditionById = mapper.getBookAndEditionById("1");
    2. System.out.println(bookAndEditionById.getEdition());


日志中显示调用2次select方法:
[mybatistest.BookMapper.getBookAndEditionById]-> Preparing: select from book where userId=?
[mybatistest.BookMapper.getBookAndEditionById]-> Parameters: 1(String)
[mybatistest.BookMapper.getBookAndEditionById]-<== Total: 1
[mybatistest.EditionMapper.getEditionById]-> Preparing: select
from edition where editId=?
[mybatistest.EditionMapper.getEditionById]-> Parameters: 1(Integer)
[mybatistest.EditionMapper.getEditionById]-<== Total: 1

  1. Book bookAndEditionById = mapper.getBookAndEditionById("1");
  2. System.out.println(bookAndEditionById.getUserId());


日志中显示调用一次select方法:
[mybatistest.BookMapper.getBookAndEditionById]-> Preparing: select * from book where userId=?
[mybatistest.BookMapper.getBookAndEditionById]-> Parameters: 1(String)
[mybatistest.BookMapper.getBookAndEditionById]-<== Total: 1

collection定义关联集合查询:

  • 创建edition类,里面创建list ```java package bean;

import java.util.List;

public class Edition { private Integer editId; private String editName; private List books;

  1. public Edition() {
  2. }
  3. public Edition(Integer editId, String editName, List<Book> books) {
  4. this.editId = editId;
  5. this.editName = editName;
  6. this.books = books;
  7. }
  8. public List<Book> getBooks() {
  9. return books;
  10. }
  11. public void setBooks(List<Book> books) {
  12. this.books = books;
  13. }
  14. public Integer getEditId() {
  15. return editId;
  16. }
  17. public void setEditId(Integer editId) {
  18. this.editId = editId;
  19. }
  20. public String getEditName() {
  21. return editName;
  22. }
  23. public void setEditName(String editName) {
  24. this.editName = editName;
  25. }
  26. @Override
  27. public String toString() {
  28. return "Edition{" +
  29. "editId=" + editId +
  30. ", editName='" + editName + '\'' +
  31. ", books=" + books +
  32. '}';
  33. }

}

  1. -
  2. 接口中创建方法:
  3. ```java
  4. public Edition getEditionAndBookListById(String editId);
  • 映射文件中的select标签:

    1. <resultMap id="editionAndBook" type="edition">
    2. <id column="editId" property="editId"></id>
    3. <id column="editName" property="editName"></id>
    4. <!--
    5. collection:定义关联集合类型的属性的封装规则
    6. ofType:指定集合里面元素的类型
    7. -->
    8. <collection property="books" ofType="book">
    9. <id column="userId" property="userId"></id>
    10. <result column="userName" property="username"></result>
    11. <result column="ustatus" property="ustatus"></result>
    12. </collection>
    13. </resultMap>
    14. <select id="getEditionAndBookListById" resultMap="editionAndBook">
    15. select e.editId,e.editName,b.userId,b.username,b.ustatus from book b LEFT JOIN edition e on b.editId=e.editId where e.editId=#{editId};
    16. </select>
  • 测试方法:
    1. EditionMapper mapper = sqlSession.getMapper(EditionMapper.class);
    2. Edition editionAndBookListById = mapper.getEditionAndBookListById("1");
    3. System.out.println(editionAndBookListById);

collection分步查询:

  • Edition和Book接口的方法:
    1. public Edition getEditionByIdAndBookByIdStep(String editId);
  1. //根据字段返回Book的list集合
  2. public List<Book> getBooksById(String editId);
  • 映射文件中的select语句: ```xml

  1. ```xml
  2. <resultMap id="booksById" type="book">
  3. <id property="userId" column="userId"></id>
  4. <result property="username" column="userName"></result>
  5. <result property="ustatus" column="ustatus"></result>
  6. </resultMap>
  7. <select id="getBooksById" resultMap="booksById">
  8. select * from book where editId=#{editId}
  9. </select>
  • 测试方法:
    1. EditionMapper mapper = sqlSession.getMapper(EditionMapper.class);
    2. Edition editionAndBookListById = mapper.getEditionByIdAndBookByIdStep("1");
    3. System.out.println(editionAndBookListById);
  • 扩展:多列的值传递过去,将多列的值封装map传递,column=“{key1=column1,key2=column2}”
    1. <collection property="books" select="mybatistest.BookMapper.getBooksById" column="editId=editId">
    2. </collection>
  • collection也可以使用延时加载,可以不用修改全局配置
    fetchType=“lazy” ,使用延时查询
    fetchType=“eager”,使用立即查询

discriminator鉴别器:

  • 可以根据列的值判断返回的对象:
    1. <resultMap id="editionAndBooks" type="edition">
    2. <id column="editId" property="editId"></id>
    3. <result column="editName" property="editName"></result>
    4. <!--
    5. column:指定判断的列名
    6. javaType:列值对应的java类型
    7. -->
    8. <discriminator javaType="string" column="editId">
    9. <case value="1" resultType="edition">
    10. <collection property="books" select="mybatistest.BookMapper.getBooksById" column="editId">
    11. </collection>
    12. </case>
    13. <case value="2" resultType="edition">
    14. <id column="editId" property="editId"></id>
    15. <result column="editName" property="editName"></result>
    16. </case>
    17. </discriminator>
    18. </resultMap>
    19. <select id="getEditionByIdAndBookByIdStep" resultMap="editionAndBooks">
    20. select * from edition where editId=#{editId}
    21. </select>

动态sql:

  • 动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
    使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
    如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

    • if
    • choose (when, otherwise)
    • trim (where, set)
    • foreach

if判断:

  • 使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。

    1. <resultMap id="myBook" type="book">
    2. <id column="userid" property="userId"></id>
    3. <result column="userName" property="username"></result>
    4. <result column="ustatus" property="ustatus"></result>
    5. </resultMap>
    6. <select id="getBookById" resultMap="myBook">
    7. select *from book
    8. <if test="userId!=null">
    9. where userId like #{userId}
    10. </if>
    11. <if test="userName!=null">
    12. and userName like #{userName}
    13. </if>
    14. <if test="ustatus!=null">
    15. and ustatus like #{ustatus}
    16. </if>
    17. </select>


使用and连接可以进行2个条件判断

  1. <if test="userName!=null and ustatus!=null">
  2. and userName like #{userName}
  3. </if>
  • 测试:
    1. Book bookById = mapper.getBookById("1","Java",null);
    2. System.out.println(bookById);


当输入为null时,里面if标签的sql就不会在数据库中执行。

  • 查询的时候如果某些条件没带可能sql拼装会有问题

    • 给where 后面加上1=1,以后的条件都and xxx ```xml

      1. -
      2. mybatis使用where标签来将所有的查询条件包括在内,mybatis就会将where标签中多出来的and或者or去掉,注意:where只会去掉第一个多出来的and或者orand放在后面sql语句错误
      3. ```xml
      4. <select id="getBookById" resultMap="myBook">
      5. select *from book
      6. <where>
      7. <if test="userId!=null">
      8. userId like #{userId}
      9. </if>
      10. <if test="userName!=null ">
      11. and userName like #{userName}
      12. </if>
      13. <if test="ustatus!=null">
      14. and ustatus like #{ustatus}
      15. </if>
      16. </where>
      17. </select>

      trim自定义截取:

      • 后面多出的and或者or,where标签不能解决

        • prefix=“ ”:前缀,trim标签体是整个字符串拼串后的结果。
          prefix给拼串后的整个字符串加一个前缀。

        • prefixOverrides=“ ”:前缀覆盖
          prefixOverrides去掉整个字符串前面多余的字符。

        • suffix=“ ”:后缀
          suffix给拼串后的整个字符串加一个后缀。

        • suffixOverrides=“ ”:后缀覆盖
          suffixOverrides去掉整个字符串后面多余的字符。

        1. <select id="getBookById" resultMap="myBook">
        2. select *from book
        3. <trim prefix="where" suffixOverrides="and">
        4. <if test="userId!=null">
        5. userId like #{userId} and
        6. </if>
        7. <if test="userName!=null ">
        8. userName like #{userName} and
        9. </if>
        10. <if test="ustatus!=null">
        11. ustatus like #{ustatus} and
        12. </if>
        13. </trim>
        14. </select>

      choose分支选择:

      • 有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

        1. <select id="getBookByChoose" resultType="book">
        2. select * from book
        3. <where>
        4. <choose>
        5. <when test="userId!=null">
        6. userId=#{userId}
        7. </when>
        8. <when test="userName!=null">
        9. userName=#{userName}
        10. </when>
        11. <when test="ustatus!=null">
        12. ustatus=#{ustatus}
        13. </when>
        14. <otherwise>
        15. 1=1
        16. </otherwise>
        17. </choose>
        18. </where>
        19. </select>

      set和if实现动态更新:

      • 创建接口
        1. public void updateBySet(Book book);
      • 配置映射文件
        1. <update id="updateBySet">
        2. update book
        3. <set>
        4. <if test="userId!=null">
        5. userId=#{userId},
        6. </if>
        7. <if test="username!=null">
        8. username=#{username},
        9. </if>
        10. <if test="ustatus!=null">
        11. ustatus=#{ustatus}
        12. </if>
        13. where userId=#{userId}
        14. </set>
        15. </update>
      • 测试:
        1. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
        2. mapper.updateBySet(new Book("3","Python","m"));
        3. sqlSession.commit();

      foreach遍历集合:

      • 动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候

      • 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach

        • item:将当前遍历出的元素赋值给指定的变量
        • separator:每个元素之间的分隔符
        • open:遍历出所有结果拼接一个结束的字符串
        • close:遍历出所有结果拼接一个结束的字符
      • 当使用可迭代对象或者数组时

        • index 是当前迭代的序号
        • item 的值是本次迭代获取到的元素
      • 当使用 Map 对象(或者 Map.Entry 对象的集合)时

        • index 是键
        • item 是值
      • Demo:

        • 接口方法:
          1. public List<Book> getBookByForeach(List<String> userId);
      • 映射文件中查询标签:
        1. <select id="getBookByForeach" resultType="book">
        2. select * from book where userId in
        3. <foreach collection="list" item="item_id" separator=","
        4. open="(" close=")">
        5. #{item_id}
        6. </foreach>
        7. </select>
      • 测试:
        1. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
        2. List<String> userIds=new ArrayList<>();
        3. userIds.add("1");
        4. userIds.add("2");
        5. List<Book> bookByForeach = mapper.getBookByForeach(userIds);
        6. for (Book book:bookByForeach){
        7. System.out.println(book);
        8. }

      foreach批量插入的两种方式:

      • 第一种(推荐):
        1. <insert id="addBooks">
        2. insert into book (userId,username,ustatus) values
        3. <foreach collection="list" item="book" separator=",">
        4. (#{book.userId},#{book.username},#{book.ustatus})
        5. </foreach>
        6. </insert>
      • 第二种:
        1. <insert id="addBooks">
        2. <foreach collection="list" item="book" separator=";">
        3. insert into book (userId,username,ustatus) values (#{book.userId},#{book.username},#{book.ustatus})
        4. </foreach>
        5. </insert>


      但需要再jdbc配置文件中设置属性支持多次插入:

      1. pro.url=jdbc:mysql://localhost:3306/user?allowMultiQueries=true
      • 测试:
        1. @Test
        2. public void test2() throws IOException {
        3. SqlSessionFactory sqlSessionFactory = sqlSessionFactory();
        4. SqlSession sqlSession = sqlSessionFactory.openSession();
        5. BookMapper mapper;
        6. List<Book> books;
        7. try {
        8. mapper = sqlSession.getMapper(BookMapper.class);
        9. books = new ArrayList<>();
        10. books.add(new Book("6","Javascrip","j"));
        11. books.add(new Book("7","go","g"));
        12. mapper.addBooks(books);
        13. } finally {
        14. sqlSession.commit();
        15. }
        16. }

      内置参数:

      • _parameter:代表整个参数

        • 单个参数:_parameter就是这个参数
        • 多个参数:参数会被封装为一个map:_parameter就是代表这个map
      • _databaseId:如果配置了databaseIdProvider标签

        • _databaseId就是代表当前数据库别名oracle,可以用来使用不同数据库执行不同的sql操作
        1. <select id="getbook" resultType="book">
        2. select * from book
        3. <if test="_paramter!=null">
        4. where userId=#{userId}
        5. </if>
        6. </select>
      1. //测试_parameter内置参数
      2. public List<Book> getbook(Book book);


      _parameter代表传入的对象,要使用对象中的内容何以直接获取,也可以通过_parameter.属性值获取

      bind绑定:

      • bind 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文,方便后文引用。

      • 使用like查询满足一个字符等等操作
        name写你拼接的变量
        value 写你要如何拼接,注意使用 ‘ ’ 来表示拼接的字符

        1. <select id="getbook" resultType="book">
        2. <bind name="username" value="username+'%'"/>
        3. select * from book
        4. <if test="_parameter!=null">
        5. where username like #{username}
        6. </if>
        7. </select>

      sql抽取可重用片段:

      • 抽取可重用的sql片段,方便后面引用

        • sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用
        • include来引用已经抽取的sql
        • include还可以自定义一些property,sql标签内部就能使用自定义的属性,include-property,取值的正确方式${prop},#{不能使用这种方式}
      • Demo: ```xml
        1. insert into book (
        2. <include refid="insertColumns"></include>
        3. ) values
        (#{book.userId},#{book.username},#{book.ustatus})

      userId,username,ustatus

      1. <a name="7266dd6f"></a>
      2. # Mybatis的缓存机制:
      3. - ![](https://gitee.com/ljlGitee001/pictures/raw/master/img/202203230004398.png#alt=image-20211121134548170)
      4. <a name="c3ab9156"></a>
      5. ### 两种缓存:
      6. -
      7. 两级缓存:
      8. - 一级缓存:(本地缓存)sqlSession级别缓存。其实就是sqlSession级别的一个Map。与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。
      9. - 一级缓存失效原因(没有使用到当前一级缓存的情况,效果就是,还需要向数据库发出查询)
      10. 1. sqlSession不同
      11. 2. sqlSession相同,查询条件不同,(当前一级缓存中还没有这个数据)
      12. 3. sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据库有影响)
      13. 4. sqlSession相同,手动清楚了一级缓存(缓存清空 sqlSessin.clearcache())
      14. -
      15. 二级缓存:
      16. -
      17. 一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中,如果会话关闭,一级缓存中的数据就会被保存到二级缓存中,就可以参照二级缓存中的内容,不同的namespace查出的数据会放在自己对应的缓存中(map)
      18. -
      19. 效果:数据会从二级缓存中获取,查出的数据会被默认先放在一级缓存中,只有会话提交或者关闭以后,一级缓存的数据才会转移到二级缓存中
      20. -
      21. 使用:
      22. 1.
      23. 开启全局二级缓存配置:
      24. ```xml
      25. <setting name="cacheEnabled" value="true"/>
      1. 2.

      去mapper.xml中配置使用二级缓存

      1. <mapper namespace="mybatistest.BookMapper">
      2. <cache></cache>


      可以自己设置二级缓存机制:

      1. -

      eviction(缓存的回收策略)

      1. - `LRU` 最近最少使用:移除最长时间不被使用的对象。
      2. - `FIFO` 先进先出:按对象进入缓存的顺序来移除它们。
      3. - `SOFT` 软引用:基于垃圾回收器状态和软引用规则移除对象。
      4. - `WEAK` 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
      5. -

      flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

      1. -

      size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

      1. -

      readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

      1. -

      type=“ ”:指定自定义缓存的全类名,实现Cache接口即可

      1. 3.

      我们的POJO需要实现序列化接口

      1. public class Book implements Serializable {
      1. 4.

      测试:

      1. SqlSession sqlSession = sqlSessionFactory.openSession();
      2. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
      3. Book book1 = mapper.getBook("1");
      4. System.out.println(book1);
      5. sqlSession.close();
      6. SqlSession sqlSession1=sqlSessionFactory.openSession();
      7. BookMapper mapper1 = sqlSession1.getMapper(BookMapper.class);
      8. Book book2 = mapper1.getBook("1");
      9. System.out.println(book2);
      10. System.out.println(book1==book2);
      11. sqlSession1.close();
      • 和缓存有关的设置/属性

        • cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)

        • 每个select标签都有useCache=“true”; false:不适用缓存(一级缓存依然使用,二级缓存不使用)

        • 每个增删改标签的:flushCache=“true” (一级二级都会清除)
          增删改执行完成后就会清除缓存;
          测试:flushCache=“true”:一级缓存就清空了,二级也会被清除
          查询标签:flushCache=“false”:如果flushCache=true每次查询都会清空缓存,缓存是没有被使用

        • sqlSession.clearCache();只是清除当前session的一级缓存

        • localCacheScope:本地缓存作用域(一级缓存session);当前会话所有数据保存在会话缓存中, 取值STATEMENT:可以禁用一级缓存

      缓存原理图解:

      • Mybatis - 图9

      与第三方包整合(ehcache)

      • 导入第三方缓存包
        1. <dependency>
        2. <groupId>net.sf.ehcache</groupId>
        3. <artifactId>ehcache-core</artifactId>
        4. <version>2.6.8</version>
        5. </dependency>
      • 导入与第三方缓存整合的适配包
        1. <dependency>
        2. <groupId>org.mybatis.caches</groupId>
        3. <artifactId>mybatis-ehcache</artifactId>
        4. <version>1.1.0</version>
        5. </dependency>
      • 在类路径下加入ehcache的配置文件:ehcache.xml ```xml
        1. <?xml version="1.0" encoding="UTF-8"?>
        <ehcache xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
        1. xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

      1. -
      2. mapper.xml中使用自定义缓存
      3. ```xml
      4. <mapper namespace="mybatistest.BookMapper">
      5. <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
      • 图解缓存机制:
        Mybatis - 图10

      Mybatis逆向工程:

      • 简介:
        Mybatis - 图11
        文档地址:
        http://mybatis.org/generator/index.html

      • 导入依赖:

        1. <dependency>
        2. <groupId>org.mybatis.generator</groupId>
        3. <artifactId>mybatis-generator-maven-plugin</artifactId>
        4. <version>1.4.0</version>
        5. </dependency>
      • 配置文件: ```xml <?xml version=”1.0” encoding=”UTF-8”?> <!DOCTYPE generatorConfiguration
        1. PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        2. "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

      1. <javaTypeResolver >
      2. <property name="forceBigDecimals" value="false" />
      3. </javaTypeResolver>
      4. <!--javaModelGenerator:指定javaBean的生成策略
      5. targetPackage:目标包名
      6. targetProject:目标工程
      7. -->
      8. <javaModelGenerator targetPackage="bean" targetProject=".\.\src">
      9. <property name="enableSubPackages" value="true" />
      10. <property name="trimStrings" value="true" />
      11. </javaModelGenerator>
      12. <!--sqlMapGenerator:sql映射生成策略-->
      13. <sqlMapGenerator targetPackage="resources" targetProject=".\.\src">
      14. <property name="enableSubPackages" value="true" />
      15. </sqlMapGenerator>
      16. <!--javaClientGenerator:指定mapper接口所在的位置-->
      17. <javaClientGenerator type="XMLMAPPER" targetPackage="dao" targetProject=".\.\src">
      18. <property name="enableSubPackages" value="true" />
      19. </javaClientGenerator>
      20. <!--table指定要逆向分析哪张表:根据表创建javaBean
      21. tableName表明
      22. domainObjectName创建javaBean对象类名
      23. -->
      24. <table tableName="book" domainObjectName="book"></table>
      25. <table tableName="edition" domainObjectName="edition"></table>
      26. </context>

      1. -
      2. 运行测试类:
      3. ```java
      4. package test;
      5. import org.junit.Test;
      6. import org.mybatis.generator.api.MyBatisGenerator;
      7. import org.mybatis.generator.config.Configuration;
      8. import org.mybatis.generator.config.xml.ConfigurationParser;
      9. import org.mybatis.generator.internal.DefaultShellCallback;
      10. import java.io.File;
      11. import java.util.ArrayList;
      12. import java.util.List;
      13. public class MybatisTest {
      14. @Test
      15. public void testMbg() throws Exception {
      16. List<String> warnings = new ArrayList<String>();
      17. boolean overwrite = true;
      18. File configFile = new File("src/main/resources/mbg.xml");
      19. ConfigurationParser cp = new ConfigurationParser(warnings);
      20. Configuration config = cp.parseConfiguration(configFile);
      21. DefaultShellCallback callback = new DefaultShellCallback(overwrite);
      22. MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
      23. myBatisGenerator.generate(null);
      24. }
      25. }


      则会逆向自动生成bean,mapper,接口方法等…

      Mybatis运行原理:

      Mybatis - 图12

      • sqlSessionFactory的初始化:(根据配置文件创建sqlSessionFactory)
        总之,把配置文件的信息解析并保存再Configuration对象中,返回并包含了Configuration的DefaultSqlSession
        Mybatis - 图13

      • openSession创建sqlSession对象:
        返回sqlSession的实现类DefaultSqlSession对象。它里面包含了Executer和Configuration:Executer会在这一步被创建
        Mybatis - 图14

      • 查询实现:
        Mybatis - 图15
        Mybatis - 图16

      • 总流程:

        1. 根据配置文件(全局,sql映射)初始化Configuration对象
        2. 创建一个DefaultSqlSession对象,它里面包含Configuration以及Executor(根据全局配置文件的defaultExecuterType创建出对应的Executor)
        3. DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy
        4. MapperProxy里面有(DefaultSqlSession)
        5. 执行增删查改的方法:

          1. 调用DefaultSqlSeeion的增删查改(Executor)
          2. 会创建一个StatementHandler对象(同时也会创建ParameterHandler和ResultRetHandler)
          3. 调用StatementHandler来预编译参数以及设置参数值,使用ParamterHandler来给sql设置参数
          4. 调用StatementHandler的增删查改的方法
          5. ResultSetHandler封装结果

      Mybatis插件:

      插件原理:

      在四大对象创建的时候:

      • 每个创建出来的对象不是直接返回的,而是interceptorChain.pluginAll(parameterHandle);

      • 获取到所有的Interceptor(拦截器)(插件需要实现的接口),调用interceptor.plugin(target);返回target包装后的对象

      • 插件机制:我们可以使用插件为目标对象创建一个代理对象:AOP(面向切面)
        我们的插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行

      插件编写:

      单个插件:

      1. 编写Interceptor的实现类
      2. 使用@Intercepts注解完成插件签名
      3. 将写好的插件注册到全局配置文件中
      1. package mybatistest;
      2. import org.apache.ibatis.executor.statement.StatementHandler;
      3. import org.apache.ibatis.plugin.*;
      4. import java.sql.Statement;
      5. import java.util.Properties;
      6. /**
      7. * 完成插件签名:
      8. * 告诉mybatis当前插件用来拦截哪个对象的哪个方法
      9. */
      10. @Intercepts(
      11. {
      12. @Signature(type = StatementHandler.class,method = "parameterize",args = Statement.class)
      13. }
      14. )
      15. public class MyFirstPlugin implements Interceptor {
      16. /**
      17. * 拦截目标对象的目标方法的执行
      18. * @param invocation
      19. * @return
      20. * @throws Throwable
      21. */
      22. @Override
      23. public Object intercept(Invocation invocation) throws Throwable {
      24. System.out.println("intercept方法:"+invocation.getMethod());
      25. //执行目标方法
      26. Object proceed = invocation.proceed();
      27. //返回执行后的返回值
      28. return proceed;
      29. }
      30. /**
      31. * plugin:包装目标对象,为目标对象创建代理对象
      32. * @param target
      33. * @return
      34. */
      35. @Override
      36. public Object plugin(Object target) {
      37. System.out.println("plugin方法:"+target);
      38. //我们可以借助Plugin的wrap方法来使用Interceptor包装我们目标对象
      39. Object wrap = Plugin.wrap(target, this);
      40. //返回为当前target创建的动态代理
      41. return wrap;
      42. }
      43. /**
      44. * setProperties:将插件注册时的property属性设置进来
      45. * @param properties
      46. */
      47. @Override
      48. public void setProperties(Properties properties) {
      49. System.out.println("插件的配置信息"+properties);
      50. }
      51. }

      全集配置文件中plugins标签则是传入里面需要使用的参数

      1. <plugins>
      2. <plugin interceptor="mybatistest.MyFirstPlugin">
      3. <property name="username" value="root"/>
      4. <property name="password" value="123456"/>
      5. </plugin>
      6. </plugins>

      插件会产生目标对象的代理对象:

      Mybatis - 图17

      多个插件:

      • 多个插件就会产生多层代理
        Mybatis - 图18

      • 创建动态代理的时候,是按照插件配置顺序创建层层代理对象。
        执行目标方法的之后,按照逆向顺序执行
        Mybatis - 图19

      PageHelper分页插件:

      • 配置拦截器信息:
        1. <!--
        2. plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
        3. properties?, settings?,
        4. typeAliases?, typeHandlers?,
        5. objectFactory?,objectWrapperFactory?,
        6. plugins?,
        7. environments?, databaseIdProvider?, mappers?
        8. -->
        9. <plugins>
        10. <!-- com.github.pagehelper为PageHelper类所在包名 -->
        11. <plugin interceptor="com.github.pagehelper.PageInterceptor">
        12. </plugin>
        13. </plugins>
      • 使用接口方法来进行测试:
        (主要演示导航页号)
        1. @Test
        2. public void pageHelperTest() throws IOException {
        3. SqlSessionFactory sqlSessionFactory=sqlSessionFactory();
        4. SqlSession session = sqlSessionFactory.openSession();
        5. BookMapper mapper = session.getMapper(BookMapper.class);
        6. try {
        7. PageHelper.startPage(3, 2);
        8. List<Book> books = mapper.getBooks();
        9. //使用pageInfo获取分页的属性,用PageInfo对结果进行包装
        10. PageInfo page = new PageInfo(books,4);
        11. for (Book book:books){
        12. System.out.println(book);
        13. }
        14. int[] navigatepageNums = page.getNavigatepageNums();
        15. for (int i = 0; i < navigatepageNums.length; i++) {
        16. System.out.println(navigatepageNums[i]);
        17. }
        18. } finally {
        19. session.close();
        20. }
        21. }


      使用PageInfo来进行封装:
      注:PageInfo中的属性:

      1. private static final long serialVersionUID = 1L;
      2. //当前页
      3. private int pageNum;
      4. //每页的数量
      5. private int pageSize;
      6. //当前页的数量
      7. private int size;
      8. //由于startRow和endRow不常用,这里说个具体的用法
      9. //可以在页面中"显示startRow到endRow 共size条数据"
      10. //当前页面第一个元素在数据库中的行号
      11. private int startRow;
      12. //当前页面最后一个元素在数据库中的行号
      13. private int endRow;
      14. //总记录数
      15. private long total;
      16. //总页数
      17. private int pages;
      18. //结果集
      19. private List<T> list;
      20. //前一页
      21. private int prePage;
      22. //下一页
      23. private int nextPage;
      24. //是否为第一页
      25. private boolean isFirstPage = false;
      26. //是否为最后一页
      27. private boolean isLastPage = false;
      28. //是否有前一页
      29. private boolean hasPreviousPage = false;
      30. //是否有下一页
      31. private boolean hasNextPage = false;
      32. //导航页码数
      33. private int navigatePages;
      34. //所有导航页号
      35. private int[] navigatepageNums;
      36. //导航条上的第一页
      37. private int navigateFirstPage;
      38. //导航条上的最后一页
      39. private int navigateLastPage;


      可以通过PageInfo对象获取属性值,但使用导航页面时需要在在构造时传入参数。

      自定义类型处理枚举类:

      • Mybatis - 图20

      • 编写枚举类 ```java package mybatistest;

      public enum BookEnum { LOGIN(100,”用户登录”),LOGINOUT(200,”用户登出”),REMOVE(300,”用户不存在”); private Integer code; private String msg;

      1. BookEnum(Integer code, String msg) {
      2. this.code = code;
      3. this.msg = msg;
      4. }
      5. public Integer getCode() {
      6. return code;
      7. }
      8. public void setCode(Integer code) {
      9. this.code = code;
      10. }
      11. public String getMsg() {
      12. return msg;
      13. }
      14. public void setMsg(String msg) {
      15. this.msg = msg;
      16. }
      17. //按照状态码返回枚举对象
      18. public static BookEnum getBookStatusByCode(Integer code){
      19. switch (code){
      20. case 100:
      21. return LOGIN;
      22. case 200:
      23. return LOGINOUT;
      24. case 300:
      25. return REMOVE;
      26. default:
      27. return LOGINOUT;
      28. }
      29. }

      }

      1. -
      2. Bean和数据库表中创建相应的属性和字段:
      3. ```java
      4. package bean;
      5. import mybatistest.BookEnum;
      6. import org.apache.ibatis.type.Alias;
      7. import java.io.Serializable;
      8. @Alias(value = "book")
      9. public class Book implements Serializable {
      10. private String userId;
      11. private String username;
      12. private String ustatus;
      13. private BookEnum bookEnum=BookEnum.LOGINOUT;
      14. public Book() {
      15. }
      16. public Book(String userId, String username, String ustatus, BookEnum bookEnum) {
      17. this.userId = userId;
      18. this.username = username;
      19. this.ustatus = ustatus;
      20. this.bookEnum = bookEnum;
      21. }
      22. public String getUserId() {
      23. return userId;
      24. }
      25. public void setUserId(String userId) {
      26. this.userId = userId;
      27. }
      28. public String getUsername() {
      29. return username;
      30. }
      31. public void setUsername(String username) {
      32. this.username = username;
      33. }
      34. public String getUstatus() {
      35. return ustatus;
      36. }
      37. public void setUstatus(String ustatus) {
      38. this.ustatus = ustatus;
      39. }
      40. @Override
      41. public String toString() {
      42. return "Book{" +
      43. "userId='" + userId + '\'' +
      44. ", username='" + username + '\'' +
      45. ", ustatus='" + ustatus + '\'' +
      46. ", bookEnum=" + bookEnum +
      47. '}';
      48. }
      49. }
      • 全局配置文件中配置相应的TypeHandler
        1. <!--配置自定义的类型处理器-->
        2. <typeHandlers>
        3. <typeHandler handler="mybatistest.MyEnumBookStatusTypeHandler" javaType="mybatistest.BookEnum"></typeHandler>
        4. </typeHandlers>


      也可以在处理莫格字段的时候告诉Mybatis用什么类型处理器

      • 保存:#{bookEnum,typeHandler=xxx}

      • 查询:

        1. <resultMap id="MyBook" type="bean.Book">
        2. ...
        3. <result column="bookEnum" property="bookEnum" typeHandler="mybatistest.MyEnumBookStatusTypeHandler"></result>
        4. </resultMap>


      注意:如果在参数位置修改TypeHandler,应该保证数据和查询用的TypeHandler是一样的。