Mybatis3学习笔记(超详细)

mybatis-3.5.3

环境(my):

  • JDK1.8
  • mysql 5.7
  • maven3.6.3
  • IDEA

1、mybatis简介

1.1、使用JDBC存在的不足

  • 代码比较多,开发效率低
  • 需要关注 Connection ,Statement, ResultSet 对象创建和销毁
  • 对 ResultSet 查询的结果,需要自己封装为 List
  • 大量的代码冗余
  • 业务代码和数据库的操作混在一起

1.2、mybatis是什么?

  • MyBatis 是一款优秀的持久层框架
  • 它支持定制化 SQL、存储过程以及高级映射。
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
  • MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old JavaObjects,普通的Java对象)映射成数据库中的记录
  • 说白了就是跟数据库交互

1.3、mybatis的历史

MyBatis框架最早的名字叫iBatis

时间发展
2001年Clinton Begin发起一个开源项目 iBatis1.0
2005年iBatis2.0
2010年iBatis3.0(iBatis在这之前都是由Apache组织管理)
2010年6月iBatis项目交由谷歌公司托管,叫做MyBatis
2013年11月整个项目迁移至GitHub

1.4、为什么使用mybatis?

  • sql语句的统一处理,统一存放到 xml 文件中(易于维护)
  • 动态sql语句
  • 减轻使用 JDBC 的复杂性,不用编写关闭资源代码
  • 结果映射
  • 解决sql语句重复问题

1.5、去哪里找mybatis?

maven仓库:

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>

Github:https://github.com/mybatis/mybatis-3/releases

官方中文文档:https://mybatis.org/mybatis-3/zh/index.html

2、mybatis入门案例

准备工作:

  • 新建maven工程
  • springdb(数据库)、student(表)
  • Student(实体类)

2.1、导入maven依赖(每次添加依赖记得手动 pom.xml右键Maven–>Reimport)

<!--添加maven依赖-->
    <dependencies>
        <!--junit测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        
        <!--添加mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        
        <!--数据库连接驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
        </dependency>
    </dependencies>

    <!--解决maven存在资源过滤问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <!--所在的目录-->
                <includes>
                    <!--包括目录下的.properties,.xml 文件都会扫描到-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <!--
        maven项目会用maven-compiler-plugin默认的jdk版本来进行j编译,
        如果不指明版本就容易出现版本不匹配的问题,可能导致编译不通过的问题
        -->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

2.2、编写mybatis的核心配置文件

位置:在resources目录下

说明:主配置文件的名称是自定义的,但是建议见名知意(mybatis-config.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">
<configuration>
    <!--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"/>
                <!--连接数据库的url字符串-->
                <property name="url"
                          value="jdbc:mysql://localhost:3306/springdb?useUnicode=true&amp;characterEncoding=utf-8"/>
                <!--访问数据库的用户名-->
                <property name="username" value="root"/>
                <!--密码-->
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

   <mappers>
        <!-- 告诉 mybatis 要执行的 sql 语句的位置 -->
        <!--mapper逐个注册SQL映射文件-->
        <mapper resource="com/zwh/dao/StudentDao.xml"/>
        <!--        <mapper class="com.zwh.dao.StudentDao"/>-->

        <!--使用批量注册:这种方式要求SQL映射文件名必须和接口名相同并且在同一目录下-->
        <!--        <package name="com.zwh.dao"/>-->
    </mappers>
</configuration>

2.3、编写mybatis工具类

分析未使用工具类的情况:(每做一个测试都要写这么多代码,你肯定好累吧,所以工具类出来了!)

@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.zwh.dao.StudentMapper.selectStudents");
    //6. 循环输出查询结果
    studentList.forEach( student -> System.out.println(student));
    //7. 关闭 SqlSession ,释放资源
    session.close();
}

mybatis工具类对象分析:

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

  • SqlSessionFactoryBuilder 类:使用 SqlSessionFactoryBuilder 对 象 的 build() 方 法创建SqlSessionFactory,创建完工厂对象后,即可被销毁。所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。

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

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

自定义工具类:

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            // 使用Mybatis第一步:获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //既然有了 SqlSessionFactory,顾名思义,我们就可以从中获得 SqlSession 的实例了。
    // SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

2.4、Dao接口及mapper配置文件

Dao接口:

public interface StudentMapper {
    // 查询所有数据
    List<Student> selectStudents();
}

mapper配置文件(接口实现类由原来的StudentDaoImpl转变为一个 Mapper配置文件):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    namespace :必须有值,自定义的唯一字符串
    推荐使用: dao 接口的全限定名称
-->
<mapper namespace="com.zwh.dao.StudentMapper">
    <!--
        <select>:  查询数据, 标签中必须是 select 语句
        id: sql 语句的自定义名称,推荐使用 dao 接口中方法名称,使用名称表示要执行的 sql 语句
        resultType:  查询语句的返回结果数据类型,使用全限定类名
    -->
    <select id="selectStudents" resultType="com.zwh.pojo.Student">
        select id,name,age from student
    </select>
</mapper>

拓展(接口规范):

  1. 接口名要和对应的映射文件的名称相同(只是后缀名不同)例如:StudentMapper—>StudentMapper.xml
  2. 接口的全限定名要和mapper映射文件的namespace 一致
  3. 接口中的方法名要和mapper映射文件中的唯一标识的id相同
  4. 接口的方法返回类型和mapper 映射文件返回的类型一致
  5. 接口的方法参数类型和mapper 映射文件输入类型要一致

2.5、junit测试

位置:test/java/com.zwh.dao.TestStudent.java

测试代码:

 @Test
    public void testSelectStudents() {
        //第一步:获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentDao mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> students = mapper.selectStudents();
        for (Student student : students) {
            System.out.println(student);
        }
        //关闭SqlSession
        sqlSession.close();
    }

测试结果:
    Student{id=1, name='张三', age=18}
    Student{id=2, name='王五', age=20}
    Student{id=3, name='张飞', age=12}

3、mybatis基本的CRUD方法

注意点:

  • 增删改需要提交事务!
  • 手动提交方式:sqlSession.commit();
  • 自动提交方式:sqlSessionFactory.openSession(true)

3.1、如何在操作SQL语句执行时,携带动态的信息

方式一:#{key}(推荐使用,防止sql注入)key是可以随便写,如果sql语句中两个以上的#{key} 需要key与传递参数的值对应

select * from student where student_name = #{name} 

预编译后,会动态解析成一个参数标记符?

select * from student where student_name = ?

方式二:${}(存在sql注入问题)

select * from student where student_name = ${name} 

而使用${}在动态解析时候,会传入参数字符串

select * from student where student_name = "lisa"

注意点:

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

接口方法:
	List<Student> selectMultiParam(@Param("personName") String name,@Param("personAge") int age);
	
mapper 文件:
	<select id="selectMultiParam" resultType="com.zwh.domain.Student">
		select id,name,email,age from student where name=#{personName} or age=#{personAge}
    </select>

3.3、select

  • 编写接口
// 根据id返回一个对象
public Student selectStudentById(Integer id);
  • mapper文件编写
<!--
        parameterType:返回值类型
        resultType:返回值类型,如果配置了typeAliases则不需要全限定类名
-->xml
    <select id="selectStudentById" resultType="student" parameterType="int">
        select id,name,age from student where id = #{id};
    </select>
  • 测试
@Test
   public void testSelectStudents() {
       //第一步:获得SqlSession对象
       SqlSession sqlSession = MybatisUtils.getSqlSession();
       StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
       Student student = mapper.selectStudentById(1);
       System.out.println(student);
       //关闭SqlSession
       sqlSession.close();
   }

3.4、insert

<!--对象中的属性,可以直接取出来-->
<insert id="addStudent" parameterType="student">
        insert into student() values (#{id},#{name},#{age});
</insert>

3.5、delete

<delete id="deleteStudent" parameterType="int">
        delete from student where id = #{id}
</delete>

3.6、update

<update id="updateStudent" parameterType="student">
        update student set name = #{name} where id = #{id}
</update>

3.7、万能map

假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当优先考虑使用Map!

编写接口:

    // 根据id修改学生姓名
    public Integer updateByMap(Map<String,Object> map);

编写mapper文件:

    <update id="updateByMap" parameterType="map">
        update student set name = #{mapName} where id = #{mapId}
    </update>

测试:

    @Test
    public void testUpdateByMap() {
        //第一步:获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("mapName", "李磊");
        map.put("mapId", 1);
        Integer integer = mapper.updateByMap(map);
        System.out.println(integer+"条数据修改成功!");
        // 增删改一定要手动提交事务
        sqlSession.commit();
        //关闭SqlSession
        sqlSession.close();
    }

拓展:

模糊查询常用两种写法

  1. Java代码执行的时候,传递通配符 % %

    List<Student> userList = mapper.getUserLike("%z%");
    
  2. 在sql拼接中使用通配符!

    select * from mybatis.user where name like "%"#{value}"%"
    

4、mybatis核心配置文件标签

4.1 properties 属性(常用)

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置

<properties resource="db.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>
  • 设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值.
<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:

  • 首先读取在 properties 元素体内指定的属性。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性

因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。

4.2 settings 设置(常用)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

常见的配置项:

在这里插入图片描述

官方给出的完整的settings元素配置:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

4.3 typeAliases 类型别名(常用)

  • 定义单个类型的别名

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
</typeAliases>

如果没有alias属性,默认可以是类名首字母小写或者是首字母大写。即使指定是大写的alias,实际上小写的也是可以的。

  • 批量定义别名

如果实体类十分多,MyBatis 会在包名下面搜索需要的 Java Bean

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

默认情况下别名可以是类名首字母小写或者是首字母大写

  • 注解实现别名

若有注解,则别名为其注解值。

@Alias("author")
public class Author {
    ...
}

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

在这里插入图片描述

4.4 typeHandlers 类型处理器

我们都知道,mybatis为我们隐藏了很多操作数据库的代码,如在预处理语句PreparedStatement中设置一个参数。

或是在执行完SQL语句后从结果集中取出数据。而这两个过程都需要合适的数据类型处理器来帮我们对数据进行正确的类型转换。

在mybatis中又是谁帮我们在做这些事情呢?那就是<typeHandlers>元素了!

关于更多的typeHandlers 详解见官方文档。

4.5 objectFactory 对象工厂

关于更多的objectFactory 详解见官方文档。

4.6 plugins 插件

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 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。

关于更多的plugins 详解见官方文档。

4.7 environments 环境(常用)

MyBatis可以配置多种环境,比如开发、测试和生产环境需要有不同的配置。

每种环境使用一个environment标签进行配置并指定唯一标识符

可以通过environments标签中的default属性指定一个环境的标识符来快速的切换环境

environment 环境变量
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
  • id:指定当前环境的唯一标识
  • transactionManager、dataSource也必须有
transactionManager 事务管理器

在 MyBatis 中有两种类型的事务管理器(也就是 type="[ JDBC|MANAGED ]")。

  • JDBC : 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED : 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
  • 自定义:实现TransactionFactory接口,type=“全类名 / 别名”
<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>
dataSource 数据源

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type="[ UNPOOLED | POOLED | JNDI]")。

UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

  • driver – 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
  • url – 这是数据库的 JDBC URL 地址。
  • username – 登录数据库的用户名。
  • password – 登录数据库的密码。
  • defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
  • defaultNetworkTimeout – 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看 java.sql.Connection#setNetworkTimeout() 的 API 文档以获取更多信息。

作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:

  • driver.encoding=UTF8

这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8encoding 属性给数据库驱动。

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

除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:

  • poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
  • poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
  • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
  • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance 之和。 默认值:3(新增于 3.4.5)
  • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
  • poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
  • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

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

  • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
  • data_source – 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:

  • env.encoding=UTF8

4.8 databaseidProvider 数据库厂商标识

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 即可。

databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName() 返回的字符串。 由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短。

<databaseIdProvider type="DB_VENDOR">
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>
  <property name="Oracle" value="oracle" />
</databaseIdProvider>

在提供了属性别名时,databaseIdProvider 的 DB_VENDOR 实现会将 databaseId 设置为数据库产品名与属性中的名称第一个相匹配的值,如果没有匹配的属性,将会设置为 “null”。 在这个例子中,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle。

4.9 mappers 映射器(常用)

既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。

在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。

你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。

  • 使用相对于类路径的资源引用
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>
  • 使用完全限定资源定位符(URL)
<mappers>
	<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
  • 使用映射器接口实现类的完全限定类名
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
  • 将包内的映射器接口实现全部注册为映射器
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>
  • 主键生成

若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),则可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上。

<insert id="insertUser" databaseId="mysql"
        useGeneratedKeys="true" keyProperty="id">
	insert .....
</insert>
  • 参数传递

  • 单个参数:我们也可以封装多个参数为map,直接传递

select id,name,age from student where id = #{id};
  • 多个参数:任意多个参数,都会被MyBatis重新包装成一个Map传入
 insert into student() values (#{id},#{name},#{age});
  • 命名参数:为参数使用@Param起一个名字,MyBatis就会将这些参数封 装进map中,key就是我们自己指定的名字
public Student selectStudent(@Param("age") int age ,@Param("name") string name);
  • POJO:当这些参数属于我们业务POJO时,我们直接传递POJO
public Student selectStudent(Student student);
  • Map:我们也可以封装多个参数为map,直接传递
public Student selectStudent(Map<String,Object> map);

5 XML映射文件

5.1 select

查询语句是 MyBatis 中最常用的元素之一——光能把数据存到数据库中价值并不大,还要能重新取出来才有用,多数应用也都是查询比修改要频繁。

MyBatis 的基本原则之一是:在每个插入、更新或删除操作之间,通常会执行多个查询操作。因此,MyBatis 在查询和结果映射做了相当多的改进。

一个简单查询的 select 元素是非常简单的。

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>

这个语句名为 selectPerson,接收一个 int( Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。

#{id},这就告诉 MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:

// 近似的 JDBC 代码
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);

select元素的属性:

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap用于引用外部 parameterMap 的属性,**目前已被废弃。**请使用行内参数映射和 parameterType 属性。
resultType期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
fetchSize这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。

5.2 insert、update、delete

数据变更语句 insert,update 和 delete 的实现非常接近。

常见元素属性:

属性描述
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 的语句;如果带和不带的语句都有,则不带的会被忽略。

下面是 insert,update 和 delete 语句的示例:

<insert id="insertAuthor">
  insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})
</insert>

<update id="updateAuthor">
  update Author set
    username = #{username},
    password = #{password},
    email = #{email},
    bio = #{bio}
  where id = #{id}
</update>

<delete id="deleteAuthor">
  delete from Author where id = #{id}
</delete>

如前所述,插入语句的配置规则更加丰富,在插入语句里面有一些额外的属性和子元素用来处理主键的生成,并且提供了多种生成方式。

首先,如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置为目标属性就 OK 了。例如,如果上面的 Author 表已经在 id 列上使用了自动生成,那么语句可以修改为:

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>

如果你的数据库还支持多行插入, 你也可以传入一个 Author 数组或集合,并返回自动生成的主键。

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username, password, email, bio) values
  <foreach item="item" collection="list" separator=",">
    (#{item.username}, #{item.password}, #{item.email}, #{item.bio})
  </foreach>
</insert>

对于不支持自动生成主键列的数据库和可能不支持自动生成主键的 JDBC 驱动,MyBatis 有另外一种方法来生成主键。

这里有一个简单(也很傻)的示例,它可以生成一个随机 ID(不建议实际使用,这里只是为了展示 MyBatis 处理问题的灵活性和宽容度):

<insert id="insertAuthor">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
  </selectKey>
  insert into Author
    (id, username, password, email,bio, favourite_section)
  values
    (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>

在上面的示例中,首先会运行 selectKey 元素中的语句,并设置 Author 的 id,然后才会调用插入语句。这样就实现了数据库自动生成主键类似的行为,同时保持了 Java 代码的简洁。

selectKey 元素描述如下:

<selectKey
  keyProperty="id"
  resultType="int"
  order="BEFORE"
  statementType="PREPARED">
属性描述
keyPropertyselectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。
resultType结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。
order可以设置为 BEFOREAFTER。如果设置为 BEFORE,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。
statementType和前面一样,MyBatis 支持 STATEMENTPREPAREDCALLABLE 类型的映射语句,分别代表 Statement, PreparedStatementCallableStatement 类型。

5.3 参数

之前见到的所有语句都使用了简单的参数形式。但实际上,参数是 MyBatis 非常强大的元素。对于大多数简单的使用场景,你都不需要使用复杂的参数。

<select id="selectUsers" resultType="User">
  select id, username, password
  from users
  where id = #{id}
</select>

对于复杂参数见官方文档。

5.4 结果映射

resultMap 元素是 MyBatis 中最重要最强大的元素。

它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。

实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。

ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

在这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上。

如果列名和属性名不能匹配上,可以在 SELECT 语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配。

<select id="selectUsers" resultType="User">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password     as "hashedPassword"
  from some_table
  where id = #{id}
</select>

在学习了上面的知识后,你会发现上面的例子没有一个需要显式配置 ResultMap,这就是 ResultMap 的优秀之处——你完全可以不用显式地配置它们。

那么如何显式的使用ResultMap呢?

<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id" />
  <result property="username" column="user_name"/>
  <result property="password" column="hashed_password"/>
</resultMap>

然后在引用它的语句中设置 resultMap 属性就行了(注意我们去掉了 resultType 属性)。

<select id="selectUsers" resultMap="userResultMap">
  select user_id, user_name, hashed_password
  from some_table
  where id = #{id}
</select>

高级映射部分见官方文档。

5.5 自动映射

正如你在前面一节看到的,在简单的场景下,MyBatis 可以为你自动映射查询结果。但如果遇到复杂的场景,你需要构建一个结果映射。 但是在本节中,你将看到,你可以混合使用这两种策略。让我们深入了解一下自动映射是怎样工作的。

当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略大小写)。 这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性。

通常数据库列使用大写字母组成的单词命名,单词间用下划线分隔;而 Java 属性一般遵循驼峰命名法约定。为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true。

甚至在提供了结果映射后,自动映射也能工作。在这种情况下,对于每一个结果映射,在 ResultSet 出现的列,如果没有设置手动映射,将被自动映射。在自动映射处理完毕后,再处理手动映射。

有三种自动映射等级:

  • NONE - 禁用自动映射。仅对手动映射的属性进行映射。
  • PARTIAL - 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射
  • FULL - 自动映射所有属性。

默认值是 PARTIAL,这是有原因的。当对连接查询的结果使用 FULL 时,连接查询会在同一行中获取多个不同实体的数据,因此可能导致非预期的映射。

无论设置的自动映射等级是哪种,你都可以通过在结果映射上设置 autoMapping 属性来为指定的结果映射设置启用/禁用自动映射。

<resultMap id="userResultMap" type="User" autoMapping="false">
  <result property="password" column="hashed_password"/>
</resultMap>

5.6 缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

基本上就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。

这些属性可以通过 cache 元素的属性来修改。比如:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

可用的清除策略有:

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

默认的清除策略是 LRU。

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

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

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

提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

5、解决属性名和字段名不一致的问题

假设数据库字段名为pwd,而实体类属性为password

5.1、采用取别名的方式解决

<select id="getUserById" resultType="com.zwh.pojo.User">
    select id,name,pwd as password from mybatis.user where id = #{id}
</select>

5.2、使用resultMap结果集映射

<!--结果集映射-->
<resultMap id="UserMap" type="User">
    <!--column数据库中的字段,property实体类中的属性-->
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>

<select id="getUserById" resultMap="UserMap">
    select * from mybatis.user where id = #{id}
</select>

6、日志

6.1、常见日志

  • SLF4J
  • LOG4J 【掌握】(常用)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 【掌握】
  • NO_LOGGING

6.2、标准日志输出

在Mybatis中具体使用那个一日志实现,在设置中设定!在mybatis核心配置文件中,配置我们的日志!

// 标准日志输出
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

6.2、Log4j日志配置

什么是Log4j?

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们也可以控制每一条日志的输出格式;
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

实现:

  1. 先导入log4j的包

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. resources下面新建log4j.properties

# Global logging configuration
log4j.rootLogger=ERROR, stdout

# MyBatis logging configuration...
log4j.logger.priv.zwh.mall.dao=DEBUG

# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

  1. 核心配置文件中配置log4j为日志的实现

        <!--采用log4j输出日志-->
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>
    

7、mybatis联合查询

7.1、一对一关系

需求:

一对一的表关系,人 和身份证唯一对应

  • sql语句
   // 身份证IDCard     cardid(主键)   address
   create table idcard(
       cardid varchar(18),
       address varchar(20)
    ) character set utf8;
    alter table idcard add constraint pk_idcard primary key(cardid);
    
    // 人表格Person     pid(主键)   pname   cardid(外键 唯一)
    create table person(
       pid int(15),
       pname varchar(30),
       cardid varchar(18) #外键 唯一
    ) character set utf8;
    alter table person add constraint pk_person primary key(pid);
    alter table person add constraint fk_person_idcard foreign key(cardid) references idcard(cardid);
    alter table person add constraint uk_person unique(cardid);
    
    insert into idcard values('110000200001010101','东城区');
    insert into idcard values('110000200002020202','西城区');
    insert into idcard values('110000200003030303','海淀区');
    
    insert into person values(1,'赵一','110000200001010101');
    insert into person values(2,'钱二','110000200002020202');
    insert into person values(3,'孙三','110000200003030303');
  • 实体类(IDCard)
//私有属性
    private String cardid;
    private String address;
    //为了根据身份证号查询 身份证和人的信息
    //多添加一个关联属性
    private Person person;

    public IDCard() {}

    public IDCard(String address) {
        this.address = address;
    }
    public IDCard(String cardid, String address, Person person) {
        this.cardid = cardid;
        this.address = address;
        this.person = person;
    }

    @Override
    public String toString() {
        return "IDCard{" +
                "cardid='" + cardid + '\'' +
                ", address='" + address + '\'' +
                ", person=" + person +
                '}';
    }

    public String getCardid() {
        return cardid;
    }
    public void setCardid(String cardid) {
        this.cardid = cardid;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Person getPerson() {
        return person;
    }
    public void setPerson(Person person) {
        this.person = person;
    }
  • 实体类(person)
  //自有属性
    private Integer pid;
    private String pname;
    //关联属性 身份证号(身份证对象中的一个属性)
    private IDCard idcard;

    public Person() {}
    public Person(Integer pid, String pname, IDCard idcard) {
        this.pid = pid;
        this.pname = pname;
        this.idcard = idcard;
    }

    @Override
    public String toString() {
        return "Person{" +
                "pid=" + pid +
                ", pname='" + pname + '\'' +
                ", idcard=" + idcard +
                '}';
    }

    public Integer getPid() {
        return pid;
    }
    public void setPid(Integer pid) {
        this.pid = pid;
    }
    public String getPname() {
        return pname;
    }
    public void setPname(String pname) {
        this.pname = pname;
    }
    public IDCard getIdcard() {
        return idcard;
    }
    public void setIdcard(IDCard idcard) {
        this.idcard = idcard;
    }

方式一:采用两次查询进行表的数据关联

  1. 接口

    // 使用两次查询的方式根据id查询人的完整信息
    public Person selectOneById(int pid);
    
  2. mapper配置文件

    <!--自己定义一个赋值的规则  id是整个赋值规则的名字 -->
        <resultMap id="selectIdCard" type="com.zwh.pojo.Person">
            <id property="pid" column="pid"></id>
            <result property="pname" column="pname"></result>
            <!--property对应本实体类的属性,属性名要一一对应-->
            <association property="idcard" javaType="IDCard" select="selectIDCardForPerson" column="cardid"></association>
        </resultMap>
        
        <!--这个select标签是为了Person对象中的那个idcard属性再次查询用的-->
        <select id="selectIDCardForPerson" resultType="com.zwh.pojo.IDCard">
            select * from idcard where cardid = #{cardid}
        </select>
        
        <!--dao调用的是下面这个标签对应的语句-->
        <select id="selectOneById" resultMap="selectIdCard">
            select pid,pname,cardid from person where pid = #{pid}
        </select>
    
  3. junit测试

    @Test
        public void selectOneByid() {
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
            Person person = mapper.selectOneById(3);
            System.out.println(person);
        }
        
    测试结果:Person{pid=3, pname='孙三', idcard=IDCard{cardid='110000200003030303', address='海淀区', person=null}}
    
    

    方式二:采用联合查询进行表的数据关联

    1. 接口

      // 采用联合查询的方式根据id查询人的完整信息
      public Person selectONeById2(int pid);
      
    2. mapper配置文件

       <!--方案二 使用联合查询,等值连接 内链接 外连接-->
          <resultMap id="selectIdCard2" type="person">
              <result property="pid" column="pid"/>
              <result property="pname" column="pname"/>
              <association property="idcard" javaType="idcard">
                  <result property="cardid" column="cardid"/>
                  <result property="address" column="address"/>
              </association>
          </resultMap>
      
          <select id="selectONeById2" resultMap="selectIdCard2">
                 select p.pid,p.pname,i.cardid,i.address from person p inner join idcard i on p.cardid = i.cardid where p.pid = #{pid};
          </select>
      
    3. Junit测试

      @Test
          public void selectOneByid2() {
              SqlSession sqlSession = MybatisUtils.getSqlSession();
              PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
              Person person = mapper.selectONeById2(3);
              System.out.println(person);
          }
          
      测试结果:Person{pid=3, pname='孙三', idcard=IDCard{cardid='110000200003030303', address='海淀区', person=null}}
      

7.2、一对多关系

需求:

通过部门编号查询 部门+员工 一个部门对象(属性 List<好多个员工对象>)

准备工作:

  • sql语句
#设计部门表格

create table dept(

	deptno int(10),

	dname varchar(20),

	loc varchar(20)
) character set utf8;
alter table dept add constraint pk_dept primary key(deptno);


insert into dept values(10,'ACCOUNTING','NEW YORK');

insert into dept values(20,'RESEARCH','DALLAS');

insert into dept values(30,'SALES','CHICAGO');


#设计员工表格

create table emp(

	empno int(4),

	ename varchar(20),

	sal float(10,2),
	deptno int(10)

) character set utf8;
alter table emp add constraint pk_emp primary key(empno);

alter table emp add constraint fk_emp_dept foreign key(deptno) references dept(deptno);

insert into emp values(7369,'SMITH',800,20);

insert into emp values(7499,'ALLEN',1600,30);

insert into emp values(7521,'WARD',1250,30);

insert into emp values(7566,'JONES',2975,20);

insert into emp values(7782,'CLARK',2450,10);

insert into emp values(7839,'KING',5000,10);

insert into emp values(7788,'SCOTT',3000,20);
  • 实体类(Dept)
//部门表
public class Dept {

    //自有属性
    private Integer deptno;
    private String dname;
    private String loc;
    //为了根据deptno部门编号查询 部门中所有的人更加方便
    //也存储一个关联属性     一个部门中有好多员工  List<Emp>
    private List<Emp> empList;


    public Dept() {}
    public Dept(Integer deptno, String dname, String loc, List<Emp> empList) {
        this.deptno = deptno;
        this.dname = dname;
        this.loc = loc;
        this.empList = empList;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptno=" + deptno +
                ", dname='" + dname + '\'' +
                ", loc='" + loc + '\'' +
                ", empList=" + empList +
                '}';
    }

    public Integer getDeptno() {
        return deptno;
    }
    public void setDeptno(Integer deptno) {
        this.deptno = deptno;
    }
    public String getDname() {
        return dname;
    }
    public void setDname(String dname) {
        this.dname = dname;
    }
    public String getLoc() {
        return loc;
    }
    public void setLoc(String loc) {
        this.loc = loc;
    }
    public List<Emp> getEmpList() {
        return empList;
    }
    public void setEmpList(List<Emp> empList) {
        this.empList = empList;
    }
}
  • 实体类(Emp)
//员工表
public class Emp {

    //自有属性
    private Integer empno;
    private String ename;
    private Float sal;
    //关联属性  deptno-->外键-->对象
    private Dept dept;

    public Emp() {}
    public Emp(Integer empno, String ename, Float sal, Dept dept) {
        this.empno = empno;
        this.ename = ename;
        this.sal = sal;
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "empno=" + empno +
                ", ename='" + ename + '\'' +
                ", sal=" + sal +
                ", dept=" + dept +
                '}';
    }

    public Integer getEmpno() {
        return empno;
    }
    public void setEmpno(Integer empno) {
        this.empno = empno;
    }
    public String getEname() {
        return ename;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public Float getSal() {
        return sal;
    }
    public void setSal(Float sal) {
        this.sal = sal;
    }
    public Dept getDept() {
        return dept;
    }
    public void setDept(Dept dept) {
        this.dept = dept;
    }
}
  • 接口
// 根据部门编号查询员工信息和部门信息
public List<Dept> selectEmpByEdeptno(Integer deptno);
  • mapper配置文件(这里我采用联合查询的方式)
 <resultMap id="selectDept" type="dept">
        <result property="deptno" column="deptno"/>
        <result property="dname" column="dname"/>
        <result property="loc" column="loc"/>
        <collection property="empList" javaType="list" ofType="emp">
            <result property="empno" column="empno"></result>
            <result property="ename" column="ename"></result>
            <result property="sal" column="sal"></result>
        </collection>
    </resultMap>

    <select id="selectEmpByEdeptno" resultMap="selectDept" parameterType="int">
        select d.deptno,d.dname,d.loc,e.empno,e.ename,e.sal from dept d inner join emp e on d.deptno = e.deptno where d.deptno = #{deptno}
    </select>
  • Junit测试
 @Test
    public void selectEmpByDeptno() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        List<Dept> depts = mapper.selectEmpByEdeptno(20);
        for (Dept dept : depts) {
            System.out.println(dept);
        }
    }
    
测试结果:Dept{deptno=20, dname='RESEARCH', loc='DALLAS', 
empList=[Emp{empno=7369, ename='SMITH', sal=800.0, dept=null}, 
Emp{empno=7566, ename='JONES', sal=2975.0, dept=null}, 
Emp{empno=7788, ename='SCOTT', sal=3000.0, dept=null}]}

7.3、多对多关系

需求:

给老师的tid 查询老师的信息 + 所教的所有学生信息

准备工作:

  • sql语句
#设计一个老师的表格
create table teacher(
	tid int(10),
	tname varchar(20),
	tsex varchar(4),
	tage int(3)
) character set utf8;
#添加主键约束
alter table teacher add constraint pk_teacher primary key(tid);
#设计一个学生的表格
create table student(
	sid int(10),
	sname varchar(20),
	ssex varchar(4),
	sage int(3)
) character set utf8;
#添加主键约束
alter table student add constraint pk_student primary key(sid);

#设计一张中间表 老师-学生的关系
create table tea_stu(
	tid int(10),
	sid int(10)
) character set utf8;
#分别设置两个列的外键约束
alter table tea_stu add constraint fk_teacher foreign key(tid) references teacher(tid);
alter table tea_stu add constraint fk_student foreign key(sid) references student(sid);
#设置联合主键
alter table tea_stu add constraint pk_tea_stu primary key(tid,sid);
#添加测试数据
insert into teacher values(1,'zzt','男',18);
insert into teacher values(2,'panda','男',58);
insert into teacher values(3,'艾薇','女',16);
insert into student values(1,'赵一','男',18);
insert into student values(2,'钱一','女',17);
insert into student values(3,'孙一','女',19);
insert into student values(4,'李一','男',18);
insert into student values(5,'周一','男',17);
insert into student values(6,'吴一','女',19);
insert into student values(7,'郑一','女',18);
insert into student values(8,'王一','男',16);
insert into tea_stu values(1,1);
insert into tea_stu values(1,2);
insert into tea_stu values(1,3);
insert into tea_stu values(1,4);
insert into tea_stu values(1,5);
insert into tea_stu values(1,6);
insert into tea_stu values(1,7);
insert into tea_stu values(1,8);
insert into tea_stu values(2,1);
insert into tea_stu values(2,2);
insert into tea_stu values(2,4);
insert into tea_stu values(2,7);
insert into tea_stu values(3,1);
insert into tea_stu values(3,2);
insert into tea_stu values(3,5);
insert into tea_stu values(3,8);
select * from teacher;
select * from student;
select t.*,s.* from teacher t,tea_stu ts,student s where t.tid = ts.tid and ts.sid = s.sid;
  • 实体类(student)
//学生类
public class Students {

    //自有属性
    private Integer sid;
    private String sname;
    private String ssex;
    private Integer sage;
    //关联属性   老师集合
    private List<Teacher> teacherList;


    public Students() {
    }

    public Students(Integer sid, String sname, String ssex, Integer sage, List<Teacher> teacherList) {
        this.sid = sid;
        this.sname = sname;
        this.ssex = ssex;
        this.sage = sage;
        this.teacherList = teacherList;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", ssex='" + ssex + '\'' +
                ", sage=" + sage +
                ", teacherList=" + teacherList +
                '}';
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public String getSsex() {
        return ssex;
    }

    public void setSsex(String ssex) {
        this.ssex = ssex;
    }

    public Integer getSage() {
        return sage;
    }

    public void setSage(Integer sage) {
        this.sage = sage;
    }

    public List<Teacher> getTeacherList() {
        return teacherList;
    }

    public void setTeacherList(List<Teacher> teacherList) {
        this.teacherList = teacherList;
    }
}
  • 实体类(Teacher)
//老师类
public class Teacher {

    //自有属性
    private Integer tid;
    private String tname;
    private String tsex;
    private Integer tage;
    //关联属性  学生集合
    private List<Students> studentsList;


    public Teacher() {
    }

    public Teacher(Integer tid, String tname, String tsex, Integer tage, List<Students> studentsList) {
        this.tid = tid;
        this.tname = tname;
        this.tsex = tsex;
        this.tage = tage;
        this.studentsList = studentsList;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "tid=" + tid +
                ", tname='" + tname + '\'' +
                ", tsex='" + tsex + '\'' +
                ", tage=" + tage +
                ", studentList=" + studentsList +
                '}';
    }

    public Integer getTid() {
        return tid;
    }

    public void setTid(Integer tid) {
        this.tid = tid;
    }

    public String getTname() {
        return tname;
    }

    public void setTname(String tname) {
        this.tname = tname;
    }

    public String getTsex() {
        return tsex;
    }

    public void setTsex(String tsex) {
        this.tsex = tsex;
    }

    public Integer getTage() {
        return tage;
    }

    public void setTage(Integer tage) {
        this.tage = tage;
    }

    public List<Students> getStudentsList() {
        return studentsList;
    }

    public void setStudentsList(List<Students> studentsList) {
        this.studentsList = studentsList;
    }
}
  • 接口
// 根据老师的tid	查询老师的信息 + 所教的所有学生信息
public List<Teacher> selectTeacher(Integer tid);
  • mapper文件
<mapper namespace="com.zwh.dao.StudentMapper">

    <resultMap id="selectT" type="teacher">
        <result property="tid" column="tid"/>
        <result property="tname" column="tname"/>
        <result property="tsex" column="tsex"/>
        <result property="tage" column="tage"/>
        <collection property="studentsList" javaType="list" ofType="students">
            <result property="sid" column="sid"/>
            <result property="sname" column="sname"/>
            <result property="ssex" column="ssex"/>
            <result property="sage" column="sage"/>
        </collection>
    </resultMap>

    <select id="selectTeacher" resultMap="selectT">
        select t.*,s.* from teacher t inner join tea_stu ts on t.tid = ts.tid inner join students s on s.sid = ts.sid where t.tid = #{tid}
    </select>

</mapper>
  • Junit测试
@Test
    public void selectTeacher() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Teacher> teachers = mapper.selectTeacher(1);
        for (Teacher teacher : teachers) {
            System.out.println(teacher);
        }
        sqlSession.close();
    }
    
测试结果(数据库结果):
	1	zzt	男	18	1	赵一	男	18
    1	zzt	男	18	2	钱一	女	17
    1	zzt	男	18	3	孙一	女	19
    1	zzt	男	18	4	李一	男	18
    1	zzt	男	18	5	周一	男	17
    1	zzt	男	18	6	吴一	女	19
    1	zzt	男	18	7	郑一	女	18
    1	zzt	男	18	8	王一	男	16

小结:

  1. 关联 - association --当接口方法返回对象时使用
  2. 集合 - collection–当接口方法返回集合时使用
  3. JavaType 用来指定实体类中属性的类型
  4. ofType 用来指定映射到List或者集合中的 pojo类型,泛型中的约束类型!

8、mybatis动态SQL

8.1、什么是动态sql

  • 动态SQL就是指根据不同的条件生成不同的SQL语句
  • 动态 SQL,主要用于解决查询条件不确定的情况
  • MyBatis 的动态 SQL 语句,与 JSTL 中的语句非常相似

8.2、准备细节

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

特别是对于小于号(<),在 XML 中是绝不能出现的。否则解析 mapper 文件会出错。

实体符号表:

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

8.3、动态SQL之if

<select id="selectStudent" resultType="student" parameterType="map">
        select * from student
            where 1 = 1
            <if test="name != null">
                and name = #{name}
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
    </select>
  • if标签的中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1 的子句
  • 而 where 后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 where,SQL 出错

8.4、动态SQL之where

<select id="selectStudent" resultType="student" parameterType="map">
        select * from student
        <where>
            <if test="name != null">
                and name = #{name}
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
        </where>
    </select>

8.5、动态SQL之ForEach

  • 标签用于实现对于数组与集合的遍历。对其使用,需要注意:
  • collection 表示要遍历的集合类型, list ,array 等。
  • open、close、separator 为对遍历内容的 SQL 拼接。
<foreach collection="集合类型" open="开始的字符" close="结束的字符"
				item="集合中的成员" separator="集合成员之间的分隔符">
         #{item 的值}
</foreach>
  • 遍历list简单类型:查询学生 id 是 1002,1005,1006
<select id="selectStudentForList" resultType="com.zwh.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>
  • 遍历list对象类型
<select id="selectStudentForList2" resultType="com.bjpowernode.domain.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>

8.6、动态SQL之代码片段

有的时候,我们可能会将一些功能的部分抽取出来,方便复用!

  • 使用SQL标签抽取公共的部分
<sql id="if-student">
    <if test="age != null">
        age = #{age}
    </if>
    <if test="name != null">
        and name = #{name}
    </if>
</sql>
  • 在需要使用的地方使用Include标签引用即可
<select id="queryBlogIF" parameterType="map" resultType="student">
    select * from student
    <where>
        <include refid="if-student"></include>
    </where>
</select>

8.7 动态SQL之choose(when,otherwise)

  • 一个条件就是一套choose、when、otherwise的组合
<select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                </when>
                <when test="author != null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views = #{views}
                </otherwise>
            </choose>
        </where>
    </select>

9、mybatis实现分页

为什么使用分页?

1、减少数据的处理量

2、mybatis的分页功能省去大部分繁杂代码

9.1、使用Limit分页

  • 语法
SELECT * from user limit startIndex,pageSize;
SELECT * from user limit 3;  #[0,n]
  • 接口
 //分页
 List<Student> getUserByLimit(Map<String,Integer> map);
  • Mapper.xml
 <!--//分页-->
<select id="getUserByLimit" parameterType="map" resultType="student">
        select * from  student limit #{startIndex},#{pageSize}
</select>
  • 测试
@Test
    public void getUserByLimit() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        HashMap<String, Integer> map = new HashMap<>();
        map.put("startIndex", 1);
        map.put("pageSize", 2);
        List<Student> userByLimit = mapper.getUserByLimit(map);
        for (Student student : userByLimit) {
            System.out.println(student);
        }
    }
    
    测试结果:
        Student{id=2, name='王五', age=20}
        Student{id=3, name='张飞', age=12}

9.2、RowBounds分页

不再使用SQL实现分页

  • 接口
//分页2
List<Student> getStudentByRowBounds();
  • mapper.xml
<!--分页-->
<select id="getStudentByRowBounds" resultMap="student">
    select * from  student
</select>
  • 测试
@Test
public void getStudentByRowBounds(){

SqlSession sqlSession = MybatisUtils.getSqlSession();

//RowBounds实现
RowBounds rowBounds = new RowBounds(1, 2);

//通过Java代码层面实现分页
List<Student> List = sqlSession.selectList("com.zwh.dao.StudentMapper.getStudentByRowBounds",null,rowBounds);

    for (User user : List) {
    	System.out.println(user);
    }
    sqlSession.close();
}

9.3、分页插件

PageHelper通用分页插件

PageHelper 支持多种数据库:

  1. Oracle
  2. Mysql
  3. MariaDB
  4. SQLite
  5. Hsqldb
  6. PostgreSQL
  7. DB2
  8. SqlServer(2005,2008)
  9. Informix
  10. H2
  11. SqlServer2012
  12. Derby
  13. Phoenix
  • maven 坐标
<dependency>
 <groupId>com.github.pagehelper</groupId>
 <artifactId>pagehelper</artifactId>
 <version>5.1.10</version>
</dependency>
  • 加入 plugin 配置
<!--
    plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
    properties?, settings?,
    typeAliases?, typeHandlers?,
    objectFactory?,objectWrapperFactory?,
    plugins?,
    environments?, databaseIdProvider?, mappers?
-->
<plugins>
	 <plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>
  • PageHelper 对象
//查询语句之前调用 PageHelper.startPage 静态方法
@Test
public void testSelect() throws IOException {
//获取第 1 页,3 条内容
	PageHelper.startPage(1,3);
 	List<Student> studentList = studentDao.selectStudents();
 	studentList.forEach( stu -> System.out.println(stu));
}

10、mybatis基于注解开发

10.1、面向接口编程

  • 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
  • 接口的本身反映了系统设计人员对系统的抽象理解。

10.2、使用注解开发

  • 注解在接口上实现
@Select("select * from student")
List<Student> getStudents();
  • 需要在核心配置文件中绑定接口!
<!--绑定接口-->
<mappers>
    <mapper class="com.zwh.dao.StuMapper"/>
</mappers>

10.3、CRUD

  • 我们可以在工具类创建的时候实现自动提交事务!
public static SqlSession  getSqlSession(){
    return sqlSessionFactory.openSession(true);
}
  • 编写接口,增加注解
public interface UserMapper {

    @Select("select * from user")
    List<User> getUsers();

    // 方法存在多个参数,所有的参数前面必须加上 @Param("id")注解
    @Select("select * from user where id = #{id}")
    User getUserByID(@Param("id") int id);


    @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
    int addUser(User user);

    
    @Update("update user set name=#{name},pwd=#{password} where id = #{id}")
    int updateUser(User user);

    
    @Delete("delete from user where id = #{uid}")
    int deleteUser(@Param("uid") int id);
}

关于@Param() 注解:

  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型的话,可以忽略,但是建议大家都加上!
  • 我们在SQL中引用的就是我们这里的 @Param() 中设定的属性名!

11 缓存

分类:

  • 一级缓存

一级缓存也叫本地缓存,与数据库同一次会话期间查询到的数据会放在本地缓存中。

以后需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库

当执行同一个查询语句(二次查询)时,第二次直接走缓存。

一级缓存失效情况(日志中走两次sql语句):

  1. sqlsession不同
  2. sqlsession相同,条件不同
  3. sqlsession相同,但是两次执行之间执行了增删改操作
  4. sqlsession相同,两次查询期间手动清空了缓存
  • 二级缓存

二级缓存(second level cache),全局作用域缓存

二级缓存默认不开启,需要手动配置

MyBatis提供二级缓存的接口以及实现,缓存实现要求 POJO实现Serializable接口

二级缓存在 SqlSession 关闭或提交之后才会生效

工作机制:

1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中。

2、如果会话关闭,一级缓存中的数据就会保存到二级缓存中。

3、不同的namespace查出的数据会放在对应的缓存中(map)

12、Lombok(懒人工具)

Lombok是什么?
使用Lombok,通过注解类,让你不再需要编写getter、equals等方法,减少样板代码的编写

使用步骤:

  1. 在IDEA中安装Lombok插件!

  2. 在项目中导入lombok的jar包

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.10</version>
    </dependency>
    
  3. 在实体类上加注解即可!

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    

Intellij idea开发的话需要安装Lombok plugin,同时设置 Setting -> Compiler -> Annotation Processors -> Enable annotation processing勾选。

13、mybatis踩坑记录

13.1、Mybatis 出现org.apache.ibatis.io不存在,org.apache.ibatis.session不存在等错误

在这里插入图片描述

原因分析:可能是IDEA缓存导致错误如果是IDEA2020,极大的可能是maven不兼容,需要使用默认的maven

解决方法:

  1. 第一步:

  2. 在这里插入图片描述

  3. 第二步:击蓝色的选项

  4. 在这里插入图片描述

  5. 重启后,打开Terminal面板

  • 分别输入以下命令:
  • mvn idea:idea
  • mvn clean
  • mvn test
  • 在这里插入图片描述

13.2、 Could not find resource mybatis-config.xml、could not find resources com/zwh/dao/StudentDao.xml

在这里插入图片描述

原因分析:

  1. 可能是配置文件没有注册
  2. 绑定接口错误
  3. Maven导出资源问题
  4. 可能还是因为IDEA缓存问题

解决方法:

  1. 8.1解决方法执行(IDEA缓存问题)

  2. 检查核心配置文件有没有注册mapper

  3. 在这里插入图片描述

  4. 存在上述两个问题还可能是父工程的影响,因此后面我所有的模块都是单独的没有构建父模块。

14 MBG逆向工程

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>untitled</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • 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>
    <!-- 数据库驱动包位置 -->
    <classPathEntry location="D:\\IDEA\\java-ee\\jdbc\\src\\lib\\mysql-connector-java-5.1.7-bin.jar"/>
    <!--这里需要将mysql坐标放入dependencies标签中-->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 生成的 Java 文件的编码 -->
        <property name="javaFileEncoding" value="UTF-8"/>
        <!-- 格式化 Java 代码 -->
        <property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/>
        <!-- 格式化 XML 代码 -->
        <property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter"/>
        <!-- 生成toString -->
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
        <!--去掉默认注释-->
        <!--其中suppressDate是去掉生成日期那行注释,suppressAllComments是去掉所有的注解-->
        <!-- type值为自定义的MyCommentGenerator-->
        <commentGenerator type="com.zwh.mbg.MyCommentGenerator">
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressDate" value="false"/>
            <property name="suppressAllComments" value="false"/>
            <!-- 是否添加数据表中字段的注释 true:是 : false:否 -->
            <property name="addRemarkComments" value="true"/>
        </commentGenerator>
        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/meyon_mall?useUnicode=true&amp;characterEncoding=UTF-8&amp;
                        zeroDateTimeBehavior=CONVERT_TO_NULL&amp;serverTimezone=Asia/Shanghai&amp;useSSL=false&amp;nullCatalogMeansCurrent=true"
                        userId="root"
                        password="asda">
            <!--useInformationSchema 实体类上添加数据表的注释 -->
            <property name="useInformationSchema" value="true"/>
        </jdbcConnection>
        <!--        <javaTypeResolver>-->
        <!--            <property name="forceBigDecimals" value="false"/>-->
        <!--        </javaTypeResolver>-->
        <!-- 生成实体的位置-->
        <javaModelGenerator targetPackage="priv.zwh.demo.pojo" targetProject="./src/main/java">
            <!-- 是否允许子包 -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- 生成 Mapper 接口的位置 -->
        <sqlMapGenerator targetPackage="priv.zwh.demo.dao" targetProject="./src/main/java">
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>
        <!--生成 Mapper XML 的位置-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="priv.zwh.demo.dao" targetProject="./src/main/java">
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>
        <!-- 要生成那些表(更改tableName和domainObjectName就可以) -->
        <table tableName="me_users" domainObjectName="User" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>
    </context>

</generatorConfiguration>
  • MyCommentGenerator
import static org.mybatis.generator.internal.util.StringUtility.isTrue;

//CommentGenerator
public class MyCommentGenerator implements CommentGenerator {


    public static void main(String[] args) {
        List<String> warnings = new ArrayList<>();
        boolean overwrite = true;
        // 给出generatorConfig.xml文件的位置,绝对地址或者类路径下
        //File configFile = new File("E:\\xxxxxxxxx\\generatorConfig.xml");
        File configFile = new File(MyCommentGenerator.class.getClassLoader().getResource("generatorConfig.xml").getFile());
        ConfigurationParser cp = new ConfigurationParser(warnings);
        try {
            Configuration config = cp.parseConfiguration(configFile);
            DefaultShellCallback callback = new DefaultShellCallback(overwrite);
            MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
            myBatisGenerator.generate(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Properties properties;

    private boolean suppressDate;

    private boolean suppressAllComments;

    /**
     * If suppressAllComments is true, this option is ignored.
     */
    private boolean addRemarkComments;

    private SimpleDateFormat dateFormat;

    public MyCommentGenerator() {
        super();
        properties = new Properties();
        suppressDate = false;
        suppressAllComments = false;
        addRemarkComments = false;
    }

    /**
     * 默认配置
     *
     * @param properties
     */
    @Override
    public void addConfigurationProperties(Properties properties) {
        this.properties.putAll(properties);

        suppressDate = isTrue(properties
                .getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_DATE));

        suppressAllComments = isTrue(properties
                .getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_ALL_COMMENTS));

        addRemarkComments = isTrue(properties
                .getProperty(PropertyRegistry.COMMENT_GENERATOR_ADD_REMARK_COMMENTS));

        String dateFormatString = properties.getProperty(PropertyRegistry.COMMENT_GENERATOR_DATE_FORMAT);
        if (StringUtility.stringHasValue(dateFormatString)) {
            dateFormat = new SimpleDateFormat(dateFormatString);
        }
    }

    /**
     * 实体类添加的注释
     *
     * @param topLevelClass
     * @param introspectedTable
     */
    @Override
    public void addModelClassComment(TopLevelClass topLevelClass,
                                     IntrospectedTable introspectedTable) {
        if (suppressAllComments || !addRemarkComments) {
            return;
        }

        topLevelClass.addJavaDocLine("/**"); //$NON-NLS-1$
        String remarks = introspectedTable.getRemarks();
        if (addRemarkComments && StringUtility.stringHasValue(remarks)) {
            String[] remarkLines = remarks.split(System.getProperty("line.separator"));  //$NON-NLS-1$
            for (String remarkLine : remarkLines) {
                topLevelClass.addJavaDocLine(" * " + remarkLine);  //$NON-NLS-1$
            }
        }
        topLevelClass.addJavaDocLine(" *"); //$NON-NLS-1$
        topLevelClass.addJavaDocLine(" * createTime:  " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        topLevelClass.addJavaDocLine(" * tableName:   " + introspectedTable.getFullyQualifiedTable().toString());
        topLevelClass.addJavaDocLine(" */"); //$NON-NLS-1$
        topLevelClass.addJavaDocLine("@Data"); //$NON-NLS-1$
    }

    @Override
    public void addClassComment(org.mybatis.generator.api.dom.java.InnerClass innerClass, IntrospectedTable introspectedTable) {

    }

    @Override
    public void addClassComment(org.mybatis.generator.api.dom.java.InnerClass innerClass, IntrospectedTable introspectedTable, boolean b) {

    }

    /**
     * 实体类的属性注释,数据库中自定义注释
     *
     * @param field
     * @param introspectedTable
     * @param introspectedColumn
     */
    @Override
    public void addFieldComment(Field field,
                                IntrospectedTable introspectedTable,
                                IntrospectedColumn introspectedColumn) {
        if (suppressAllComments) {
            return;
        }
        field.addJavaDocLine("/**"); //$NON-NLS-1$
        String remarks = introspectedColumn.getRemarks();
        if (addRemarkComments && StringUtility.stringHasValue(remarks)) {
            String[] remarkLines = remarks.split(System.getProperty("line.separator"));  //$NON-NLS-1$
            for (String remarkLine : remarkLines) {
                field.addJavaDocLine(" * " + remarkLine);  //$NON-NLS-1$
            }
        }
        field.addJavaDocLine(" */"); //$NON-NLS-1$
    }

    @Override
    public void addFieldComment(Field field, IntrospectedTable introspectedTable) {
        if (suppressAllComments) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        field.addJavaDocLine("/**"); //$NON-NLS-1$
        sb.append(introspectedTable.getFullyQualifiedTable());
        field.addJavaDocLine(" */"); //$NON-NLS-1$
    }


    @Override
    public void addGeneralMethodComment(Method method, IntrospectedTable introspectedTable) {
    }


    @Override
    public void addEnumComment(InnerEnum innerEnum, IntrospectedTable introspectedTable) {
    }

    // 不需要get方法的注释
    @Override
    public void addGetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
    }

    // 不需要set方法的注释
    @Override
    public void addSetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
    }

    @Override
    public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable, Set<FullyQualifiedJavaType> imports) {
    }

    @Override
    public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {
    }

    @Override
    public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable, Set<FullyQualifiedJavaType> imports) {

    }

    @Override
    public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {
    }

    @Override
    public void addClassAnnotation(org.mybatis.generator.api.dom.java.InnerClass innerClass, IntrospectedTable introspectedTable, Set<FullyQualifiedJavaType> set) {

    }


    @Override
    public void addComment(XmlElement xmlElement) {

    }

    @Override
    public void addRootComment(XmlElement rootElement) {
    }

    @Override
    public void addJavaFileComment(CompilationUnit compilationUnit) {
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值