01-basic

原生JDBC实现CURD问题

  1. 编码繁琐
  2. 将结果集映射成对象
  3. 性能不太好,连接池、缓存
  4. SQL语句和java代码的耦合度特别高
64971704416724_.pic

1. 认识框架

  • 框架(Framework)对于java来说,就是一系列为了解决特定问题而定义的一系列接口和实现类,在组织框架代码时,使用了一系列优秀的设计模式,使代码无论在性能上还是API操作上得到很大提升。框架可以看做是项目开发的半成品,基本的底层操作已经封装完毕,通过框架,程序员可以从底层代码中解脱出来,专注于业务逻辑的完成和性能的优化。框架规定了你的应用的体系结构。它定义了整体结构,类和对象的分割,各部分的主要责任,类和对象怎么协作,以及控制流程。框架预定义了这些设计参数,以便于应用设计者或实现者能集中精力于应用本身的特定细节
  • 如果将开发完成的软件比作是一套已经装修完毕的新房,那框架就好比是一套已经修建好的毛坯房。用户直接购买毛坯房,建筑质量和户型合理有保证,还省去了自己建造房屋的时间,一举多得
64981704416772_.pic
  • 框架还有一个作用是约束。同样的技术解决同样的问题会产生不同流程和风格的解决方案,而采用一种框架其实就是限制用户必须使用其规定的方案来实现,可以降低程序员之间沟通以及日后维护的成本
  • 常用的基于JavaEE的三大开源框架,已经从SSH、SSH2过渡到了SSM:SpringMVC、Spring、MyBatis --->>> SpringBoot
  • 总之,框架是一个半成品,已经对基础的代码进行了封装并提供相应的API,开发者使用框架就是直接调用封装好的API可以省去很多代码编写,从而提高工作效率和开发速度

2. 认识ORM

  • ORM(Object-Relationl Mapping)对象关系映射,作用是在关系型数据库和对象之间作一个映射,在具体的数据库操作时,只要像平时操作对象一样就可以了
64991704416793_.pic
  1. 什么是“持久化”
    • 持久(Persistence),即把数据(eg:内存中的Obj)保存到可永久保存的存储设备中(eg:磁盘)。持久化主要应用:将内存中的数据存储到关系型的数据库中
  2. 什么是 “持久层“
    • 持久层(Persistence Layer),即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。之前JDBC访问数据库的DAO层,MyBatis访问数据库的mapper层,都是持久层

3. 认识MyBatis

  • MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由Apache Software Foundation迁移到了Google Code,且改名为MyBatis。2013年11月迁移到GitHub。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架
  • MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码、手动设置参数、获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的POJOs(Plain Ordinary Java Object,普通的Java对象)映射成数据库中的记录
65001704417845_.pic
  • MyBatis是一个持久层的半自动ORM映射框架。其本质是对JDBC的封装。使用MyBatis重点需要程序员编写SQL命令,不需要写一行JDBC代码

4. MyBatis初次使用

创建一个空项目。这里不显示项目名,可以先close项目后,再次open即可

4171704417896_.pic

MyBatis官网说明文档open in new window

1. pom.xml

<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>com.listao</groupId>
   <artifactId>mybatis</artifactId>
   <version>1.0-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>mybatis</name>
   <url>http://maven.apache.org</url>

   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>

   <dependencies>
      <!-- MysqlConnector -->
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>8.0.28</version>
      </dependency>
      <!-- mybatis核心jar包 -->
      <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.6</version>
      </dependency>
      <!-- junit -->
      <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.13.1</version>
         <scope>test</scope>
      </dependency>
      <!-- lombok -->
      <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <version>1.18.12</version>
         <scope>provided</scope>
      </dependency>
      <!-- 日志的依赖log4j1 -->
      <!--<dependency>-->
      <!--    <groupId>log4j</groupId>-->
      <!--    <artifactId>log4j</artifactId>-->
      <!--    <version>1.2.17</version>-->
      <!--</dependency>-->
      <!-- log4j-core为log4j2 -->
      <dependency>
         <groupId>org.apache.logging.log4j</groupId>
         <artifactId>log4j-core</artifactId>
         <version>2.12.4</version>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
               <source>8</source>
               <target>8</target>
            </configuration>
         </plugin>
      </plugins>
   </build>
</project>

2. sql

-- auto-generated definition
create table mybatis_bonus
(
    ENAME varchar(10)  null,
    JOB   varchar(9)   null,
    SAL   double(7, 2) null,
    COMM  double(7, 2) null
);

-- auto-generated definition
create table mybatis_dept
(
    DEPTNO int(2)      not null primary key,
    DNAME  varchar(14) null,
    LOC    varchar(13) null
);

INSERT INTO `mybatis_dept` (`DEPTNO`, `DNAME`, `LOC`) VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO `mybatis_dept` (`DEPTNO`, `DNAME`, `LOC`) VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO `mybatis_dept` (`DEPTNO`, `DNAME`, `LOC`) VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO `mybatis_dept` (`DEPTNO`, `DNAME`, `LOC`) VALUES (40, 'OPERATIONS', 'BOSTON');

-- auto-generated definition
create table mybatis_emp
(
    EMPNO    int(4)       not null primary key,
    ENAME    varchar(10)  null,
    JOB      varchar(9)   null,
    MGR      int(4)       null,
    HIREDATE date         null,
    SAL      double(7, 2) null,
    COMM     double(7, 2) null,
    DEPTNO   int(2)       null,
    constraint FK_DEPTNO
        foreign key (DEPTNO) references dept (deptno)
);

INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800.00, NULL, 20);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600.00, 300.00, 30);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250.00, 500.00, 30);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975.00, NULL, 20);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250.00, 1400.00, 30);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850.00, NULL, 30);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450.00, NULL, 10);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000.00, NULL, 20);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5000.00, NULL, 10);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500.00, 0.00, 30);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100.00, NULL, 20);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950.00, NULL, 30);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000.00, NULL, 20);
INSERT INTO `mybatis_emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300.00, NULL, 10);

-- auto-generated definition
create table mybatis_project_record
(
    empno int(4) not null,
    pid   int(2) not null,
    primary key (empno, pid),
    constraint fk_emp_pro
        foreign key (empno) references emp (empno)
            on update cascade on delete cascade,
    constraint fk_project_pro
        foreign key (pid) references mybatis_projects (pid)
            on update cascade on delete cascade
);

INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 1);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7521, 1);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 2);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7499, 2);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7521, 2);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 3);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7499, 3);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7521, 3);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 4);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7499, 4);

-- auto-generated definition
create table mybatis_projects
(
    pid   int(2) auto_increment primary key,
    pname varchar(20) not null,
    money int         null
);

INSERT INTO `mybatis_project` (`pid`, `pname`, `money`) VALUES (1, ' ***大学OA', 500000);
INSERT INTO `mybatis_project` (`pid`, `pname`, `money`) VALUES (2, '学生选课系统', 100000);
INSERT INTO `mybatis_project` (`pid`, `pname`, `money`) VALUES (3, '讲师测评系统', 20000);
INSERT INTO `mybatis_project` (`pid`, `pname`, `money`) VALUES (4, '线上问答系统 ', 20000);

-- auto-generated definition
create table mybatis_salgrade
(
    GRADE int          not null primary key,
    LOSAL double(7, 2) null,
    HISAL double(7, 2) null
);

3. sqlMapConfig.xml

4181704418144_.pic

resources/sqlMapConfig.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>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://ip:port/mydb?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="******"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 加载mapper映射文件 -->
    <mappers>
        <mapper resource="com/msb/mapper/DeptMapper.xml"/>
    </mappers>

</configuration>

4. DeptMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="DeptMapper">

    <!-- public List<Dept> findAll(){} -->
    <select id="findAll" resultType="com.listao.pojo.Dept" >
        select * from mybatis_dept
    </select>

</mapper>

5. junit

public class T1_initial {

    private SqlSession sqlSession;

    @Before
    public void init() throws IOException {
        SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory factory = ssfb.build(resourceAsStream);
        sqlSession = factory.openSession();
    }

    @After
    public void release() {
        // 关闭SQLSession
        sqlSession.close();
    }

    @Test
    public void initial() {
        // 调用SQL语句
        List<Dept> depts = sqlSession.selectList("findAll");
        depts.forEach(System.out::println);
    }

}

3. MyBatis配置

  • 核心配置文件中标签是有顺序的
image-20240106221806722

1. log4j

  • 增加sql日志输出
  • sqlMapConfig.xml中配置MyBatis所使用的具体日志实现。如果不指定将自动搜索
<settings>
    <!-- 指定mybatis日志方式,如果不指定,自动查找处理 -->
    <setting name="logImpl" value="LOG4J2"/>
</settings>
设置名描述有效值默认值
logImpl指定MyBatis所用日志的具体实现,未指定时将自动查找SLF4J|LOG4J(3.5.9 起废弃)|LOG4J2|JDK_LOGGING|COMMONS_LOGGING|STDOUT_LOGGING|NO_LOGGING未设置

1. log4j1

<!-- 日志的依赖log4j1 -->
<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
</dependency>

resources/log4j.properties

# 1.定义全局日志级别调试阶段推荐debug,error warn info debug
# stdout:控制台
# logfile:文件持久化
log4j.rootLogger=debug,stdout
# 日志控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
# 日志持久化
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=/Users/listao/mca/listao_boot/mybatis/log/ooxx.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n

2. log4j2

只支持xml和json配置文件

<!-- log4j-core为log4j2 -->
<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-core</artifactId>
   <version>2.12.4</version>
</dependency>

resources/log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
   <Appenders>
      <Console name="Console" target="SYSTEM_ERR">
         <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n"/>
      </Console>

      <RollingFile name="RollingFile" filename="log/ooxx.log"
                   filepattern="${logPath}/%d{YYYYMMddHHmmss}-fargo.log">
         <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n"/>
         <Policies>
            <SizeBasedTriggeringPolicy size="10 MB"/>
         </Policies>
         <DefaultRolloverStrategy max="20"/>
      </RollingFile>

   </Appenders>
   <Loggers>
      <!-- 控制台打印、文件打印 -->
      <Root level="DEBUG">
         <AppenderRef ref="Console"/>
         <!--<AppenderRef ref="RollingFile" />-->
      </Root>
   </Loggers>
</Configuration>

2. 事务配置

 <environments default="development">
     <environment id="development">
         <transactionManager type="JDBC"/>
         <dataSource type="POOLED">
             <property name="driver" value="${jdbc_driver}"/>
             <property name="url" value="${jdbc_url}"/>
             <property name="username" value="${jdbc_username}"/>
             <property name="password" value="${jdbc_password}"/>
         </dataSource>
     </environment>
 </environments>
  • 核心配置文件中envirment,通过transactionManager配置事务的处理策略
  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚功能,它依赖从数据源获得的连接来管理事务作用域。默认情况下,为了与某些驱动程序兼容,它在关闭连接时启用自动提交。然而,对于某些驱动程序来说,启用自动提交不仅是不必要的,而且是一个代价高昂的操作。因此,从 3.5.10 版本开始,你可以通过将 "skipSetAutoCommitOnClose" 属性设置为 "true" 来跳过这个步骤。eg:
<transactionManager type="JDBC">
    <property name="skipSetAutoCommitOnClose" value="true"/>
</transactionManager>
  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(eg:JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将closeConnection属性设置为false来阻止默认的关闭行为。eg:
<transactionManager type="MANAGED">
    <property name="closeConnection" value="false"/>
</transactionManager>
  • 如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置

3. 映射文件加载方式

  1. mapper映射文件的文件路径导入,使用的mapper标签的resource属性
  2. 网络资源路径,使用的mapper标签的url属性
  3. 接口的全限定名导入,使用的是mapper标签的class属性(基于接口的代理模式开发)
  4. 包扫描形式加载所有的mapper映射文件,使用的是package标签
<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口全部注册为映射器 -->
<mappers>
   <package name="org.mybatis.builder"/>
</mappers>

4. 实体类别名处理

<typeAliases>
    <typeAlias alias="Author" type="domain.blog.Author"/>
    <typeAlias alias="Blog" type="domain.blog.Blog"/>
    <typeAlias alias="Comment" type="domain.blog.Comment"/>
    <typeAlias alias="Post" type="domain.blog.Post"/>
    <typeAlias alias="Section" type="domain.blog.Section"/>
    <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
<!-- 设置实体类别名 -->
<typeAliases>
    <!-- 通过包扫描给所有包下实体类起别名 -->
    <package name="com.msb.pojo"/>
</typeAliases>

映射文件的resultTypeparamterType上就可以使用别名了

1. 内建别名

别名映射的类型
_bytebyte
_char (since 3.5.10)char
_character (since 3.5.10)char
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
char (since 3.5.10)Character
character (since 3.5.10)Character
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
bigintegerBigInteger
objectObject
date[]Date[]
decimal[]BigDecimal[]
bigdecimal[]BigDecimal[]
biginteger[]BigInteger[]
object[]Object[]
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

5. 外部DB连接信息

resources/jdbc.properties

jdbc_driver=com.mysql.cj.jdbc.Driver
jdbc_url=jdbc:mysql://ip:port/mca?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc_username=******
jdbc_password=******
<properties resource="jdbc.properties"/>

4. 传统DAO模式开发

普通模式,也称为传统DAO模式。定义接口和实现类。在实现类中,用SQLSession对象调用select, insert, delete, update等方法实现。目前极为少见

1. sqlSession查询三种方式

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.listao.mapper.EmpMapper">

   <select id="selOne" resultType="emp" >
      select * from mybatis_emp where empno = 7499
   </select>

   <select id="selList" resultType="emp">
      select * from mybatis_emp
   </select>

   <!-- 数据中的某一列作为键,整条数据封装的map作为值 -->
   <select id="selMap" resultType="map">
      select * from mybatis_emp
   </select>

</mapper>
 @Test
 public void selectOne() {
     // 查询单个对象
     Emp emp = sqlSession.selectOne("selOne");
     System.out.println(emp);

 }

 @Test
 public void selectList() {
     // 查询多个对象的List集合
     List<Emp> emps = sqlSession.selectList("com.listao.mapper.EmpMapper.selList");
     emps.forEach(System.out::println);
 }

 @Test
 public void selectMap() {
     // 查询多个对象的Map集合
     Map<Integer, HashMap<String, Object>> empMap = sqlSession.selectMap("selMap", "EMPNO");
     empMap.forEach((key, value) -> {
         System.out.println(key + ": " + value);
     });
 }

2. sqlSession传递参数三种方式

  1. 单个基本数据类型
  2. 多个基本数据类型组成map
  3. 引用类型
    <!--
        1. 单个基本数据类型作为方法参数
            #{}中可以随便写,遵循见名知意
    -->
    <!--<select id="selByEmpno" resultType="emp" useCache="true" flushCache="false">-->
    <select id="selByEmpno" resultType="emp" >
        select * from mybatis_emp where empno = #{empno}
    </select>

    <!-- 2. 参数是map,{}写键的名字 -->
    <select id="selByDeptnoAndSal2" resultType="emp" parameterType="map" >
        select * from mybatis_emp where deptno = #{deptno} and sal >= #{sal}
    </select>

    <!-- 3. 单个引用类型,{}使用对象的属性名 -->
    <select id="selByDeptnoAndSal3" resultType="emp" parameterType="emp" >
        select * from emp where deptno = #{deptno} and sal >= #{sal}
    </select>
    @Test
    public void oneArg() {
        // 测试单个基本数据类型作为参数
        Emp emp = sqlSession.selectOne("selByEmpno", 7499);
        System.out.println(emp);
    }

    @Test
    public void mapArg() {
        // 测试Map集合作为参数
        Map<String, Object> args = new HashMap<>();
        args.put("deptno", 20);
        args.put("sal", 3000.0);
        List<Emp> emps = sqlSession.selectList("selEmpByDeptnoAndSal", args);
        emps.forEach(System.out::println);
    }

    @Test
    public void objArg() {
        // 测试Map集合作为参数
        Emp arg = new Emp();
        arg.setDeptno(10);
        arg.setSal(2000.0);
        List<Emp> emps = sqlSession.selectList("selEmpByDeptnoAndSal2", arg);
        emps.forEach(System.out::println);
    }

3. sqlSession进行DML操作

    <insert id="insEmp" >
        insert into mybatis_emp values (DEFAULT, #{ename}, #{job}, #{mgr}, #{hiredate}, #{sal}, #{comm}, #{deptno})
    </insert>

    <update id="updEnameByEmpno" >
        update mybatis_emp set ename = #{ename} where empno = #{empno}
    </update>

    <update id="delByEmpno" >
        delete from mybatis_emp where empno = #{empno}
    </update>
    /**
     * 增、删、改 要提交事务
     * 1. sqlSession.commit(); // 手动提交事务
     * 2. sqlSession=factory.openSession(true); // 设置事务自动提交
     */
    @Test
    public void insert() {
        Emp emp = new Emp(null, "按住啦Baby", "SALESMAN", 7839, new Date(), 3100.0, 200.0, 10);
        int row = sqlSession.insert("insEmp", emp);
        System.out.println("row = " + row);

        sqlSession.commit();
    }

    @Test
    public void update() {
        Emp emp = new Emp();
        emp.setEname("晓明");
        emp.setEmpno(7935);
        int row = sqlSession.update("updEnameByEmpno", emp);
        System.out.println("row = " + row);
        sqlSession.commit();
    }

    @Test
    public void delete() {
        int row = sqlSession.delete("delByEmpno", 7935);
        System.out.println("row = " + row);
        sqlSession.commit();
    }

5. MyBatis代理模式开发

  • MyBatis前面已经完成了对Emp表的CRUD操作,都是由SqlSession调用自身方法发送SQL命令并得到结果的。缺点:
    1. 不管是selectOne(), selectList(), selectMap(),都是通过SQLSession对象的API完成增、删、改、查,都只提供一个查询参数。多个参数,需要封装到JavaBean或者Map中
    2. 返回值类型较固定
    3. 只提供了映射文件,没有提供数据库操作的接口,不利于后期的维护扩展
  • MyBatis中还提供了另外一种成为Mapper代理(接口绑定)的操作方式。在实际开发中也使用该方式
  • 优点:
    1. 有接口,模块之间更规范了
    2. 参数的处理多样了,接口的形参数列表由自己决定
    3. 通过代理模式由mybatis提供接口的实现类对象,不用写实现类了

1. Mapper代理实现查询

<!--
    1. 接口名、Mapper映射为文件名必须一致
    2. Mapper映射文件的namespace必须是接口的全路径名
    3. sql的id必须是接口对应方法名
    4. Mapper映射文件和接口编译之后放在同一个目录下
-->
<select id="selList" resultType="emp">
    select * from mybatis_emp
</select>
package com.listao.mapper;

public interface EmpMapper {

    List<Emp> selList();
}
@Test
public void selList() {
    EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
    List<Emp> emps = empMapper.selList();
    emps.forEach(System.out::println);
}

2. 代理模式浅析

  • mybatis是如何通过代理模式实现查询的
  • 底层使用了动态代理模式,动态创建一个EmpMapper的一个代理对象并赋给接口引用。MyBatis中不需要显式提供Mapper接口的实现类
  • EmpMapper.selAll()代理对象的方法底层调用了sqlSession.selectList()

6. 代理模式开发

1. 入参

  1. 单个基本数据类型
  2. 多个基本数据类型
  3. 单个引用数据类型
  4. map集合数据类型
  5. 多个引用数据类型
<!--
    1. 接口名、Mapper映射为文件名必须一致
    2. Mapper映射文件的namespace必须是接口的全路径名
    3. sql的id必须是接口对应方法名
    4. Mapper映射文件和接口编译之后放在同一个目录下
-->
<select id="selList" resultType="emp">
    select * from mybatis_emp
</select>

<!--
    1. 单个基本数据类型作为方法参数
        #{}中可以随便写,遵循见名知意
-->
<!--<select id="selByEmpno" resultType="emp" useCache="true" flushCache="false">-->
<select id="selByEmpno" resultType="emp" >
    select * from mybatis_emp where empno = #{empno}
</select>

<!--
    多个基本数据类型`(@Param("detpno") int deptno, @Param("sal") double sal);`
        1. 方式1:arg*     (arg0, arg1, arg2) 数字是索引,从0开始
        2. 方式2:param*   (param1, param2, param3) 数字是编号,从1开始
        3. 使用别名:@Param注解使用别名后,不能再使用arg*,可以继续使用param*
-->
<select id="selByDeptnoAndSal" resultType="emp">
    <!-- select * from emp where deptno =#{arg0} and sal >= #{arg1} -->
    <!-- select * from emp where deptno =#{param1} and sal >= #{param2} -->
    select * from emp where deptno = #{deptno} and sal >= #{sal}
</select>

<!-- 2. 参数是map,{}写键的名字 -->
<select id="selByDeptnoAndSal2" resultType="emp" parameterType="map" >
    select * from mybatis_emp where deptno = #{deptno} and sal >= #{sal}
</select>

<!-- 3. 单个引用类型,{}使用对象的属性名 -->
<select id="selByDeptnoAndSal3" resultType="emp" parameterType="emp" >
    select * from mybatis_emp where deptno = #{deptno} and sal >= #{sal}
</select>

<!--
    多个引用类型作为方法参数
        用@Param定义了别名,那么就不能使用(arg*.属性名),但是可以使用(param*.属性名)、(别名.属性名)
-->
<select id="selByDeptnoAndSal4" resultType="emp"  >
    <!-- select * from emp where deptno =#{arg0.deptno} and sal >= #{arg1.sal} -->
    select * from emp where deptno = #{param1.deptno} and sal >= #{param2.sal}
    <!-- select * from emp where deptno =#{emp1.deptno} and sal >= #{emp2.sal}-->
</select>
public interface EmpMapper {

    List<Emp> selList();

    Emp selByEmpno(int empno);

    List<Emp> selByDeptnoAndSal(@Param("deptno") int deptno, @Param("sal") double sal);

    List<Emp> selByDeptnoAndSal2(Map<String, Object> map);

    List<Emp> selByDeptnoAndSal3(Emp emp);

    List<Emp> selByDeptnoAndSal4(@Param("emp1") Emp emp1, @Param("emp2") Emp emp2);

}
@Test
public void selList() {
    EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
    List<Emp> emps = empMapper.selList();
    emps.forEach(System.out::println);
}

@Test
public void selByEmpno() {
    EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = empMapper.selByEmpno(7499);
    System.out.println("emp = " + emp);
}

@Test
public void selByDeptnoAndSal() {
    EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
    List<Emp> emps = empMapper.selByDeptnoAndSal(20, 3000.0);
    emps.forEach(System.out::println);
}

@Test
public void selByDeptnoAndSal2() {
    EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
    Map<String, Object> map = new HashMap<>();
    map.put("deptno", 20);
    map.put("sal", 3000.0);
    List<Emp> emps = empMapper.selByDeptnoAndSal2(map);
    emps.forEach(System.out::println);
}

@Test
public void selByDeptnoAndSal3() {
    EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = new Emp();
    emp.setDeptno(20);
    emp.setSal(3000.0);
    List<Emp> emps = empMapper.selByDeptnoAndSal3(emp);
    emps.forEach(System.out::println);
}

@Test
public void selByDeptnoAndSal4() {
    EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
    Emp empa = new Emp();
    empa.setDeptno(10);
    Emp empb = new Emp();
    empb.setSal(3000.0);
    List<Emp> emps = empMapper.selByDeptnoAndSal4(empa, empb);
    emps.forEach(System.out::println);
}

2. 模糊查询

  • 在映射文件中使用concat()函数来连接参数和通配符
  • 对于特殊字符。eg:<,不能直接书写,使用字符实体替换
<select id="selByLikeEname" resultType="emp">
    select * from emp where ename like concat('%', #{ename}, '%')
</select>
/**
 * 模糊查询
 */
List<Emp> selByLikeEname(String ename);
@Test
public void selByEname() {
    EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
    List<Emp> emps = empMapper.selByLikeEname("a");
    emps.forEach(System.out::println);
}

3. 主键自增回填

  • MySQL支持主键自增。完成添加后需要立刻获取刚刚自增的主键
  • 在很多应用场景中,需要新增数据后获取到新增数据的主键值
    • 主键自定义,通过UUID或时间戳等方式生成唯一主键,把这个值当做主键值。在分布式场景中应用较多
    • select max(主键) from获取主键最大值。这种方式多线程访问情况下可能出现问题
    • select @@identity获取最新生成主键。要求这条SQL必须在insert操作之后,且数据库连接没有关闭
<insert id="addDept" useGeneratedKeys="true" keyProperty="deptno">
    insert into dept values (DEFAULT, #{dname}, #{loc})
</insert>

<insert id="addDept2" >
    <selectKey order="AFTER" keyProperty="deptno"  resultType="int">
        select @@identity
    </selectKey>
    insert into dept values(DEFAULT, #{dname}, #{loc})
</insert>
@Test
public void insDept(){
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    Dept dept = new Dept(null, "JAVA", "北京");
    System.out.println("dept.getDeptno() = " + dept.getDeptno());

    deptMapper.insDept(dept);
    sqlSession.commit();
    System.out.println("dept.getDeptno() = " + dept.getDeptno());
}


@Test
public void insDept2(){
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    Dept dept = new Dept(null, "JAVA", "北京");
    System.out.println("dept.getDeptno() = " + dept.getDeptno());

    deptMapper.insDept2(dept);
    sqlSession.commit();
    System.out.println("dept.getDeptno() = " + dept.getDeptno());
}
  • 方式1
    • useGeneratedKeys:表示要使用自增的主键
    • keyProperty:表示把自增的主键赋给JavaBean的哪个成员变量
  • 方式2
    • order:取值AFTER|BEFORE,表示在新增之后|之前执行<selectKey>中的SQL命令
    • keyProperty:执行select @@identity后结果填充到哪个属性中
    • resultType:结果类型

4. DML操作

<insert id="insEmp" >
    insert into mybatis_emp values (DEFAULT, #{ename}, #{job}, #{mgr}, #{hiredate}, #{sal}, #{comm}, #{deptno})
</insert>

<update id="updEnameByEmpno" >
    update mybatis_emp set ename = #{ename} where empno = #{empno}
</update>

<update id="delByEmpno" >
    delete from mybatis_emp where empno = #{empno}
</update>
int insEmp(Emp emp);

int updEnameByEmpno(@Param("empno") int empno, @Param("ename") String ename);

int delByEmpno(int empno);
@Test
public void insEmp() {
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    mapper.insEmp(new Emp(null, "TOM", "SALESMAN", 7521, new Date(), 2314.0, 100.0, 10));
    sqlSession.commit();
}

@Test
public void updEnameByEmpno() {
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    mapper.updEnameByEmpno(7938, "TOM");
    sqlSession.commit();
}

@Test
public void delByEmpno() {
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    mapper.delByEmpno(7938);
    sqlSession.commit();
}

7. 动态SQL

  • MyBatis中动态SQL编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的
  • MyBatis也可以在注解中配置SQL,但是由于注解功能受限,尤其是对于复杂的SQL语句,可读性很差,所以较少使用

1. if

List<Emp> selByCondition(Emp emp);
 <select id="selByCondition" resultType="emp">
     select * from emp where 1 = 1
     <if test="empno != null">
         and empno = #{empno}
     </if>
     <if test="ename != null and ename != ''">
         and ename like concat('%', #{ename}, '%')
     </if>
     <if test="job != null and job != ''">
         and job = #{job}
     </if>
     <if test="mgr != null">
         and mgr = #{mgr}
     </if>
     <if test="hiredate != null">
         and hiredate = #{hiredate}
     </if>
     <if test="sal != null">
         and sal = #{sal}
     </if>
     <if test="comm != null">
         and comm = #{comm}
     </if>
     <if test="deptno != null">
         and deptno = #{deptno}
     </if>
 </select>

2. where

用于处理where关键字和and

<select id="selByCondition" resultType="emp">
   select * from emp
   <where>
      <if test="empno != null">
         and empno = #{empno}
      </if>
      <if test="ename != null and ename != ''">
         and ename like concat('%', #{ename}, '%')
      </if>
      <if test="job != null and job != ''">
         and job = #{job}
      </if>
      <if test="mgr != null">
         and mgr = #{mgr}
      </if>
      <if test="hiredate != null">
         and hiredate = #{hiredate}
      </if>
      <if test="sal != null">
         and sal = #{sal}
      </if>
      <if test="comm != null">
         and comm = #{comm}
      </if>
      <if test="deptno != null">
         and deptno = #{deptno}
      </if>
   </where>
</select>

3. choose

前面的when条件成立,后面的when就不再判断了

<select id="selByCondition2" resultType="emp">
    select * from emp
     <where>
         <choose>
             <when test="empno != null">
                 and empno = #{empno}
             </when>
             <when test="ename != null and ename != ''">
                 and ename like concat('%', #{ename}, '%')
             </when>
             <when test="job != null and job != ''">
                 and job = #{job}
             </when>
             <when test="mgr != null">
                 and mgr = #{mgr}
             </when>
             <when test="hiredate != null">
                 and hiredate = #{hiredate}
             </when>
             <when test="sal != null">
                 and sal = #{sal}
             </when>
             <when test="comm != null">
                 and comm = #{comm}
             </when>
             <when test="deptno != null">
                 and deptno = #{deptno}
             </when>
             <otherwise>
                 1 = 1
             </otherwise>
         </choose>
     </where>
</select>

4. set

int updEmpByCondtion(Emp emp);
<update id="updEmpByCondtion" >
    update mybatis_emp
    <set>
        <if test="ename != null and ename != '' ">
            , ename =#{ename}
        </if>
        <if test="job != null and ename != '' ">
            , job = #{job}
        </if>
        <if test="mgr != null ">
            , mgr = #{mgr}
        </if>
        <if test="hiredate != null ">
            , hiredate = #{hiredate}
        </if>
        <if test="sal != null ">
            , sal = #{sal}
        </if>
        <if test="comm != null ">
            , comm = #{comm}
        </if>
        <if test="deptno != null ">
            , deptno = #{deptno}
        </if>
    </set>
    where empno = #{empno}
</update>

5. trim

Trim标签处理 set

<update id="updEmpByCondtion2" >
   update mybatis_emp
   <!--
       prefix:动态添加的前缀
       prefixOverrides:动态删除的前缀
       suffix:动态添加的后缀
       suffixOverrides:动态删除的后缀
    -->
   <trim prefix="set" prefixOverrides=",">
      <if test="ename != null and ename != ''">
         , ename = #{ename}
      </if>
      <if test="job != null and ename != ''">
         , job = #{job}
      </if>
      <if test="mgr != null">
         , mgr = #{mgr}
      </if>
      <if test="hiredate != null">
         , hiredate = #{hiredate}
      </if>
      <if test="sal != null">
         , sal = #{sal}
      </if>
      <if test="comm != null">
         , comm = #{comm}
      </if>
      <if test="deptno != null">
         , deptno = #{deptno}
      </if>
   </trim>
   where empno = #{empno}
</update>

Trim标签,处理where

<select id="selByCondition" resultType="emp">
    select * from emp
    <trim prefix="where" prefixOverrides="and">
        <if test="empno != null">
            and empno = #{empno}
        </if>
        <if test="ename != null and ename != ''">
            <bind name="likePattern" value="'%'+ename+'%'"/>
            and ename like #{likePattern}
        </if>
        <if test="job != null and job != ''">
            and job = #{job}
        </if>
        <if test="mgr != null">
            and mgr = #{mgr}
        </if>
        <if test="hiredate != null">
            and hiredate = #{hiredate}
        </if>
        <if test="sal != null">
            and sal = #{sal}
        </if>
        <if test="comm != null">
            and comm = #{comm}
        </if>
        <if test="deptno != null">
            and deptno = #{deptno}
        </if>
    </trim>
</select>

6. bind

一般用于处理模糊查询的模板

<select id="selByLikeEname" resultType="emp">
    <bind name="likePattern" value="'%'+param1+'%'"/>
    select * from mybatis_emp where ename like #{likePattern}
</select>

7. sql

<sql id="empColumn">empno, ename, job, mgr, hiredate, sal, comm, deptno</sql>
<sql id="baseSelect">
    select <include refid="empColumn"/>
    from mybatis_emp
</sql>
<select id="selByCondition" resultType="emp">
    <include refid="baseSelect"/>
    <trim prefix="where" prefixOverrides="and">
        <if test="empno != null">
            and empno = #{empno}
        </if>
        <if test="ename != null and ename != ''">
            <bind name="likePattern" value="'%'+ename+'%'"/>
            and ename like #{likePattern}
        </if>
        <if test="job != null and job != ''">
            and job = #{job}
        </if>
        <if test="mgr != null">
            and mgr = #{mgr}
        </if>
        <if test="hiredate != null">
            and hiredate = #{hiredate}
        </if>
        <if test="sal != null">
            and sal = #{sal}
        </if>
        <if test="comm != null">
            and comm = #{comm}
        </if>
        <if test="deptno != null">
            and deptno = #{deptno}
        </if>
    </trim>
</select>

8. foreach

<!--
    collection=""  遍历的集合(list)、数组(array)
    separator=""   多个元素取出时,分隔字符
    open=""        拼接开头
    close=""       拼接结尾
    item=""        中间变量名
-->
<select id="selByEmpnos1" resultType="emp">
    select * from mybatis_emp where empno in
    <foreach collection="array" separator="," open="(" close=")" item="item">
        #{item}
    </foreach>
</select>

<select id="selByEmpnos2" resultType="emp">
    select * from mybatis_emp where empno in
    <foreach collection="list" separator="," open="(" close=")" item="item">
        #{item}
    </foreach>
</select>

8. 手动处理映射关系

public class Emp implements Serializable {

    private Integer empno;
    private String ename;
    // DB映射字段
    private String mapping_name;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    private Integer deptno;
    private Dept dept;
}
<!--
    手动处理DB查询字段、封装实体类属性之间的映射关系
        1. 主键一般使用id属性
        2. 当属性名、数据表字段名相同,可以不写映射关系
-->
<resultMap id="empMap" type="emp">
    <!--<id property="empno" column="empno"/>-->
    <result property="mapping_name" column="ename" javaType="" jdbcType=""/>
    <!--<result property="job" column="job"/>-->
    <!--<result property="sal" column="sal"/>-->
    <!--<result property="hiredate" column="hiredate"/>-->
    <!--<result property="mgr" column="mgr"/>-->
    <!--<result property="comm" column="comm"/>-->
    <!--<result property="deptno" column="deptno"/>-->
</resultMap>
<select id="selByEmpno1" resultMap="empMap" >
    select * from mybatis_emp where empno = #{empno}
</select>

9. MyBatis多表查询

1. 关联查询

image-20240107195244458

数据准备:创建项目表和项目记录表

-- auto-generated definition
create table mybatis_projects
(
    pid   int(2) auto_increment primary key,
    pname varchar(20) not null,
    money int         null
);

INSERT INTO `mybatis_project` (`pid`, `pname`, `money`) VALUES (1, ' ***大学OA', 500000);
INSERT INTO `mybatis_project` (`pid`, `pname`, `money`) VALUES (2, '学生选课系统', 100000);
INSERT INTO `mybatis_project` (`pid`, `pname`, `money`) VALUES (3, '讲师测评系统', 20000);
INSERT INTO `mybatis_project` (`pid`, `pname`, `money`) VALUES (4, '线上问答系统 ', 20000);

-- auto-generated definition
create table mybatis_project_record
(
    empno int(4) not null,
    pid   int(2) not null,
    primary key (empno, pid),
    constraint fk_emp_pro
        foreign key (empno) references emp (empno)
            on update cascade on delete cascade,
    constraint fk_project_pro
        foreign key (pid) references mybatis_projects (pid)
            on update cascade on delete cascade
);


INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 1);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7521, 1);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 2);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7499, 2);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7521, 2);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 3);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7499, 3);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7521, 3);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 4);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7499, 4);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 1);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7521, 1);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 2);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7499, 2);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7521, 2);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 3);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7499, 3);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7521, 3);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7369, 4);
INSERT INTO `mybatis_project_record` (`empno`, `pid`) VALUES (7499, 4);

1. 一对一

根据编号查询员工信息及所在的部门信息

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Emp implements Serializable {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    private Integer deptno;

    // Dept对象作为自己的属性
    private Dept dept;
}
<resultMap id="empJoinDept" type="emp">
    <!-- 设置emp的八个属性的映射关系 -->
    <id property="empno" column="empno"/>
    <result property="ename" column="ename"/>
    <result property="job" column="job"/>
    <result property="sal" column="sal"/>
    <result property="hiredate" column="hiredate"/>
    <result property="mgr" column="mgr"/>
    <result property="comm" column="comm"/>
    <result property="deptno" column="deptno"/>
    <!--
        association:处理一对一
            property:emp类的属性名
            javaType:用哪个类的对象给属性赋值
    -->
    <association property="dept" javaType="dept">
        <id column="deptno" property="deptno"/>
        <result column="dname" property="dname"/>
        <result column="loc" property="loc"/>
    </association>
</resultMap>
<select id="selEmpJoinDeptByEmpno" resultMap="empJoinDept" >
    select * from
        mybatis_emp e
            left join mybatis_dept d
                      on e.deptno = d.deptno
    where empno = #{empno}
</select>
@Test
public void oneToOne() {
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = mapper.selEmpJoinDeptByEmpno(7499);
    System.out.println(emp);
}

2. 一对多

根据部门号查询部门信息及该部门的所有员工信息

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept implements Serializable {
    private Integer deptno;
    private String dname;
    private String loc;

    // Emp的List集合作为属性
    private List<Emp> empList;
}
<resultMap id="deptJoinEmps" type="dept">
    <id column="deptno" property="deptno"/>
    <result column="dname" property="dname"/>
    <result column="loc" property="loc"/>
    <!-- collection:一对多关系的标签 -->
    <collection property="empList" ofType="emp" >
        <!-- emp八个属性映射关系 -->
        <id property="empno" column="empno"/>
        <result property="ename" column="ename"/>
        <result property="job" column="job"/>
        <result property="sal" column="sal"/>
        <result property="hiredate" column="hiredate"/>
        <result property="mgr" column="mgr"/>
        <result property="comm" column="comm"/>
        <result property="deptno" column="deptno"/>
    </collection>
</resultMap>
<select id="selDeptJoinEmpsByDeptno" resultMap="deptJoinEmps">
    select *
    from mybatis_dept d
             left join mybatis_emp e on d.deptno = e.deptno
    where d.deptno = #{deptno}
</select>
@Test
public void oneToMany() {
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    Dept dept = mapper.selDeptJoinEmpsByDeptno(20);
    System.out.println("dept = " + dept);
}

3. 多对多

根据项目编号查询项目信息,以及参与到该项目之中的所有的员工信息

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Project  implements Serializable {
    private Integer pid;
    private String pname;
    private Integer money;

    // 一对多
    private List<ProjectRecord> projectRecords;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProjectRecord implements Serializable {
    private Integer empno;
    private Integer pid;

    // 一对一
    private Emp emp;
}
<resultMap id="projectJoinEmps" type="project">
    <id column="pid" property="pid"/>
    <result column="pname" property="pname"/>
    <result column="money" property="money"/>
    <!-- 一对多collection -->
    <collection property="projectRecords" ofType="projectRecord">
        <id column="empno" property="empno"/>
        <id column="pid" property="pid"/>
        <!-- 一对一association -->
        <association property="emp" javaType="emp">
            <id property="empno" column="empno"/>
            <result property="ename" column="ename"/>
            <result property="job" column="job"/>
            <result property="sal" column="sal"/>
            <result property="hiredate" column="hiredate"/>
            <result property="mgr" column="mgr"/>
            <result property="comm" column="comm"/>
            <result property="deptno" column="deptno"/>
        </association>
    </collection>
</resultMap>
<select id="selProjectJoinEmpsByPid" resultMap="projectJoinEmps">
    select * from
        mybatis_project p
            left join mybatis_project_record pr
                      on p.pid = pr.pid
            left join mybatis_emp e
                      on e.empno = pr.empno
    where p.pid= #{pid}
</select>
@Test
public void manyToMany() {
    ProjectMapper mapper = sqlSession.getMapper(ProjectMapper.class);
    Project project = mapper.selProjectJoinEmpsByPid(2);
    System.out.println("project = " + project);
}

2. 级联查询

级联查询,顾名思义,就是利于DB表间的外键关联关系进行自动的级联查询操作

1. 立即加载

  • 查询20号部门及其该部门员工信息
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept implements Serializable {

    private Integer deptno;
    private String dname;
    private String loc;

    // 组合一个Emp的List集合作为属性
    private List<Emp> empList;
}
<!--
    select="com.msb.mapper.EmpMapper.selEmpsByDeptno":调用的另一个SQL语句
    javaType="list":实体类的属性数据类型
    column="deptno":给另一个SQL语句传入的参数列
    jdbcType="INTEGER":参数对应JDBC的数据类型
    fetchType="eager":加载方式:eager-积极加载,lazy-延迟加载
-->
<resultMap id="deptJoinEmpsMap" type="dept">
    <id property="deptno" column="deptno"/>
    <result property="dname" column="dname"/>
    <result property="loc" column="loc"/>

    <collection property="empList"
                select="com.listao.mapper.EmpMapper.selEmpsByDeptno"
                javaType="list"
                column="deptno"
                jdbcType="INTEGER"
                fetchType="lazy"
    >
    </collection>
</resultMap>
<select id="selDeptByDeptno" resultMap="deptJoinEmpsMap">
    select * from mybatis_dept where deptno = #{deptno}
</select>
<select id="selEmpsByDeptno" resultType="emp">
    select * from mybatis_emp where deptno = #{deptno}
</select>
@Test
public void selDeptByDetpno_eager()   {
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    Dept dept = deptMapper.selDeptByDeptno(20);
    System.out.println("dept = " + dept);
}

2. 延迟加载

  • 延迟加载,又称按需加载。延迟加载的内容等到真正使用时才去进行加载(查询)。多用在关联对象或集合中
  • 延迟加载的好处:先从单表查询、需要时再从关联表去关联查询,大大降低数据库在单位时间内的查询工作量,将工作在时间上的分配更加均匀,而且单表要比关联查询多张表速度要快
  1. 全局开关:在sqlMapConfig.xml中打开延迟加载的开关。配置完成后所有的associationcollection元素都生效
    • lazyLoadingEnabled:是否开启延迟加载。是Mybatis是否启用懒加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
    • aggressiveLazyLoading:当开启时,任何方法的调用都会懒加载对象的所有属性。否则,每个属性会按需加载
<settings>
     <setting name="lazyLoadingEnabled" value="true"/>
     <setting name="aggressiveLazyLoading" value="true"/>
 </settings>
  1. 局部开关:指定的associationcollection元素中配置fetchType属性
    • eager:表示立刻加载
    • lazy:表示延迟加载。将覆盖全局延迟设置

3. 总结

  • resultMap中的常见属性
属性描述
columnDB的列名、列别名
property映射的JavaBean属性名
jdbcTypecolumn在DB表中的类型。只在insert, update, delete时针对允许空的列有用。JDBC需要这项,MyBatis不需要
javaTypeproperty的类型,一个完整的类名,或一个类型别名。如果你匹配的是一个JavaBean,那MyBatis通常会自行检测到
typeHandler属性可以覆写类型处理器,实现javaType, jdbcType之间相互转换。一般可以省略,会探测到使用的什么类型的typeHandler进行处理
fetchType自动延迟加载
selectassociation、collection的属性,使用哪个查询查询属性的值,要求指定namespace+id的全名称
ofTypecollection的属性,指明集合中元素的类型(即泛型类型)
  • 级联查询、多表查询的比较及其选择
级联查询多表查询
SQL语句数量多条一条
性能性能低性能高
延迟加载立即加载、延迟加载只有立即加载
灵活性更灵活不灵活
SQL难易度简单复杂
选择依据简单、灵活高性能
  • ResultType, ResultMap使用场景
    • 单表查询并且封装的实体和DB的字段一一对应(resultType)
    • 如果实体封装的属性和DB的字段不一致(resultMap)
    • N+1级联查询(resultMap)
    • 多表关联查询(resultMap)
  • 一对一关联映射
    • DB层次:主键关联或者外键关联(参看之前内容)
    • MyBatis层次:在映射文件的设置双方均使用association即可,用法相同
  • 一对多关联映射
    • DB层次:外键参考当前表的主键
    • MyBatis层次:按照一对多处理,但是增加的属性都写到一个实体类中,增加的映射也都写到一个映射文件中
  • 多对多映射
    • DB层次:引入一个中间表将多对多转为两个一对多
    • MyBatis层次
      1. 在映射文件的设置双方均使用collection即可,不用引入中间类
      2. 引入中间类和中间类的映射文件,按照两个一对多处理

10. MyBatis注解开发

@Select("select * from mybatis_dept where deptno = #{deptno}")
Dept selByDeptno(int deptno);

@Update("update mybatis_dept set dname = #{dname}, loc = #{loc} where deptno = #{deptno}")
int updDept(Dept dept);

@Insert("insert into mybatis_dept values(DEFAULT, #{dname}, #{loc})")
int insDeptAnno(Dept dept);

@Delete("delete from mybatis_dept where deptno = #{deptno}")
int delDept(int deptno);
@Test
public void insDept() {
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    int row = deptMapper.insDeptAnno(new Dept(null, "总部", "北京"));
    sqlSession.commit();
}

@Test
public void updDept() {
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    int row = deptMapper.updDept(new Dept(43, "后勤", "北京文教园"));
    sqlSession.commit();
}

@Test
public void selByDetpno() {
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    Dept dept = deptMapper.selDeptByDeptno(43);
    System.out.println("dept = " + dept);
}

@Test
public void delDept() {
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    int row = deptMapper.delDept(43);
    sqlSession.commit();
}
  • XML优点
    1. 类和类之间的解耦
    2. 利于修改。直接修改XML文件,无需到源代码中修改
    3. 配置集中在XML中,对象间关系一目了然,利于快速了解项目和维护
    4. 容易和其他系统进行数据交换
  • 注解优点
    1. 简化配置
    2. 使用起来直观且容易,提升开发效率
    3. 类型安全,编译器进行校验,不用等到运行期才会发现错误
    4. 注解的解析可以不依赖于第三方库,可以直接使用Java自带的反射
  • 注解缺点
    1. 使用注解没有实现Java代码和SQL语句的解耦
    2. 无法实现SQL语句的动态拼接
    3. 多表查询时定制ResultMap比较麻烦

11. 缓存

  • 一种临时存储少量数据至内存、磁盘的一种技术。减少数据的加载次数,可以降低工作量,提高程序响应速度
  • 缓存的重要性不言而喻。Mybatis的缓存将相同查询条件的SQL语句执行一遍后,将所得到的结果存内存或某种缓存介质当中,当下次遇到一模一样的查询SQL时不在执行SQL,而是直接从缓存中获取结果,减少服务器的压力;尤其是在查询越多、缓存命中率越高的情况下,使用缓存对性能的提高更明显
  • MyBatis允许使用缓存,缓存一般放置在高速读/写的存储器上,eg:服务器的内存,能够有效的提供系统性能
  • MyBatis分为一级缓存、二级缓存。当数据量大时,可以借助一些第三方缓存框架、Redis缓存来协助保存Mybatis的二级缓存数据
  • 一级存储是SqlSession上的缓存,默认开启;二级缓存是在SqlSessionFactory(namespace)上的缓存。默认没有开启
4311704418334_.pic

1. 一级缓存

  • 一级存储是SqlSession上的缓存,默认开启。是一种内存型缓存,不要求实体类对象实现Serializable接口
  • 缓存中的数据使用键值对形式存储数据
  • namespace + sqlid + args + offset的hash值作为键 --->>> 查询结果作为值
4321704418337_.pic
<!-- 默认开启 -->
<select id="selByEmpno" resultType="emp" useCache="true" flushCache="false">
  <!--<select id="selByEmpno" resultType="emp">-->
  select * from mybatis_emp where empno = #{empno}
</select>
@Test
public void one_level_cache() {
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = mapper.selByEmpno(7521);
    System.out.println("emp = " + emp);

    // 增、删、改时会调用,自动清空缓存
    // sqlSession.commit();

    EmpMapper mapper2 = sqlSession.getMapper(EmpMapper.class);
    Emp emp2 = mapper2.selByEmpno(7521);
    System.out.println("emp2 = " + emp2);

    System.out.println("emp == emp2 = " + (emp == emp2));
    System.out.println("mapper == mapper2 = " + (mapper == mapper2));
}

2. 二级缓存

  • 二级缓存是以namespace为标记的缓存,由一个SqlSessionFactory创建的SqlSession之间共享缓存数据。默认并不开启
  • 创建了两个SqlSession,执行相同的SQL,尝试让第二个SqlSession使用第一个SqlSession查询后缓存的数据
  • MyBatis的二级缓存的缓存介质多种多样,而并不一定是在内存中,所以需要对JavaBean对象实现序列化接口
  • 二级缓存是以namespace为单位,不同namespace下的操作互不影响
  • 加入Cache元素后,会对相应命名空间所有的select元素查询结果进行缓存,而insert, update, delete操作是会清空整个namespace的缓存
4331704418342_.pic
public class Emp implements Serializable { }
<mapper namespace="com.listao.mapper.EmpMapper">
    <cache/>

    <!--<select id="selByEmpno" resultType="emp" useCache="true" flushCache="false">-->
    <select id="selByEmpno" resultType="emp">
        select * from mybatis_emp where empno = #{empno}
    </select>
</mapper>
@Test
public void two_level_cache() {
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = mapper.selByEmpno(7521);
    System.out.println("emp = " + emp);

    // SqlSession提交之后,才会将查询的结果放入二级缓存
    sqlSession.commit();

    EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class);
    Emp emp2 = mapper2.selByEmpno(7521);
    System.out.println("emp2 = " + emp2);

    System.out.println("(emp == emp2) = " + (emp == emp2));
}
  • SqlSession的close(), commit()后才会将该SqlSession的查询结果从一级缓存中放入二级缓存
  • 执行结果显示进行了两次对数据库的SQL查询,说明二级缓存并没有开启。需要进行如下步骤完成开启。

1. 全局开关

  • cacheEnabled的默认值就是true,可以省略
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

2. 局部开关

  • 在要开启二级缓存的mapper文件中开启缓存
<mapper namespace="com.listao.mapper.EmpMapper">
    <cache/>
</mapper>
<cache type="" readOnly="" eviction="" flushInterval="" size="" blocking=""/>
属性含义默认值
type自定义缓存类,要求实现org.apache.ibatis.cache.Cache接口null
readOnly是否只读
true - 给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改
false - 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全
false
eviction缓存策略
LRU(默认) – 最近最少使用:移除最长时间不被使用的对象
FIFO – 先进先出:按对象进入缓存的顺序来移除它们
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象
LRU
flushInterval刷新间隔,毫秒为单位。默认为null,也就是没有刷新间隔,只有执行update、insert、delete语句才会刷新null
size缓存对象个数1024
blocking是否使用阻塞性缓存BlockingCache
true - 在查询缓存时锁住对应的Key,如果缓存命中了则会释放对应的锁,否则会在查询DB以后再释放锁,保证只有一个线程到DB中查找指定key对应的数据
false - 不使用阻塞性缓存,性能更好
false

在加入Cache元素的前提下让个别select元素不使用缓存,使用useCache属性,设置为false

  • useCache:控制当前sql语句是否启用缓存
  • flushCache:控制当前sql执行一次后是否刷新缓存
<select id="findByEmpno" resultType="emp" useCache="true" flushCache="false">

3. 三方缓存

  • 分布式缓存框架:系统为了提高系统并发和性能,一般对系统进行分布式部署(集群部署方式)不适用分布缓存, 缓存的数据在各个服务单独存储,不方便系统开发。所以要使用分布式缓存对缓存数据进行集中管理,ehcache, redis, memcache缓存框架
  • Ehcache:是一种广泛使用的开源java分布式缓存。主要面向通用缓存、javaEE、轻量级容器。具有内存和磁盘存储功能。被用于大型复杂分布式web_application
  • 三方缓存作为二级缓存使用

1. pom.xml

<!-- 三方缓存 -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.2</version>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.1</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.2</version>
</dependency>

2. 缓存类型指定

  • 去各自的sql映射文件里,开启二级缓存,并把缓存类型指定为EhcacheCache
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

3. ehcache.xml

缓存配置文件:resources/ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="true" monitoring="autodetect"
         dynamicConfig="true">

    <diskStore path="/Users/listao/tmp/ehcache"/>

    <!--  Cache配置
        name:Cache的唯一标识
        maxElementsInMemory:内存中最大缓存对象数
        maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大
        eternal:Element是否永久有效,一但设置了,timeout将不起作用
        overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中
        timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大
        timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0,也就是element存活时间无穷大
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。可以设置为FIFO(先进先出)或LFU(较少使用)
    -->
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"/>
</ehcache>

12. 逆向工程

  • MyBatis的一个主要的特点就是需要程序员编写SQL,表太多的话,会很麻烦,所以MyBatis官方提供了一个逆向工程,可以针对单表自动生成MyBatis执行所需要的代码(包括***mapper.xml***mapper.javapojo)。包含了基本的CURD功能

1. pom.xml

<!-- 代码生成工具jar -->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.2</version>
</dependency>
<!-- 日志包,方便查看执行信息-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.6.1</version>
</dependency>

2. generatorConfig.xml

resources/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>
   <context id="testTables" targetRuntime="MyBatis3">
      <commentGenerator>
         <!-- 是否去除自动生成的注释 true:是 : false:否 -->
         <property name="suppressAllComments" value="true" />
      </commentGenerator>
      <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
      <!-- <jdbcConnection driverClass="com.mysql.jdbc.Driver"
         connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
         password="123">
      </jdbcConnection> -->
       <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
         connectionURL="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"
         userId="root"
         password="root">
      </jdbcConnection>

      <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
         NUMERIC 类型解析为java.math.BigDecimal -->
      <javaTypeResolver>
         <property name="forceBigDecimals" value="false" />
      </javaTypeResolver>

      <!-- targetProject:生成PO类的位置 -->
      <javaModelGenerator targetPackage="com.msb.pojo"
         targetProject=".\src">
         <!-- enableSubPackages:是否让schema作为包的后缀 -->
         <property name="enableSubPackages" value="false" />
         <!-- 从数据库返回的值被清理前后的空格 -->
         <property name="trimStrings" value="true" />
      </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
      <sqlMapGenerator targetPackage="com.msb.mapper"
         targetProject=".\src">
         <!-- enableSubPackages:是否让schema作为包的后缀 -->
         <property name="enableSubPackages" value="false" />
      </sqlMapGenerator>
      <!-- targetPackage:mapper接口生成的位置 -->
      <javaClientGenerator type="XMLMAPPER"
         targetPackage="com.msb.mapper"
         targetProject=".\src">
         <!-- enableSubPackages:是否让schema作为包的后缀 -->
         <property name="enableSubPackages" value="false" />
      </javaClientGenerator>
      <!-- 指定数据库表 -->

      <table tableName="dept" domainObjectName="Dept"
       enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" selectByExampleQueryId="false" >
               <columnOverride column="id" javaType="Integer" />
         </table>

   </context>
</generatorConfiguration>

3. log4j.properties

resources/log4j.properties

# 1.定义全局日志级别调试阶段推荐debug,error warn info debug
# stdout:控制台
# logfile:文件持久化
log4j.rootLogger=debug,stdout
# 日志控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
# 日志持久化
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=/Users/listao/mca/listao_boot/mybatis/log/ooxx.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n

4. Generator_main

package com.listao;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;


import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class Generator_main {

    public void generator() throws Exception {
        List<String> warnings = new ArrayList<>();
        boolean overwrite = true;

        // 相对路径,总项目的根目录
        File configFile = new File("mybatis/src/main/resources/generatorConfig.xml");

        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);

    }

    public static void main(String[] args) throws Exception {
        GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
        generatorSqlmap.generator();
    }
}

13. main目录配置文件打包

<build>
    <!-- maven将项目源码中的xml文件也进行编译,并放到编译目录中 -->
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>