正在连接海拉鲁...

Mybatis

框架

软件的半成品

概述

  1. 是一个持久层框架,使用SQL语句将实体类映射到数据表,是一个半自动的ORM实现。
    • O:对象
    • R:关系型数据库
    • M:映射
    • Hibernate:全自动的ORM实现
    • SSH:Struts2、Spring、Hibernate
    • SSM:SpringMVC、Spring、Mybatis
  2. 历史
    • 最早是Apache的一个开源的项目iBatis
    • 2010年变更为MyBatis
  3. 特点
    • 外部化SQL语句:把sql语句与java代码分离
    • 封装化SQL语句:把sql语句封装到单独的文件中,方便管理

使用

  1. 创建一个maven项目

  2. 配置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.neu</groupId>
        <artifactId>maven-demo</artifactId>
        <packaging>war</packaging>
        <version>1.0-SNAPSHOT</version>
    
        <build>
            <plugins>
                <!-- 资源文件拷贝插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>2.7</version>
                    <configuration>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
                <!-- java编译插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.2</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>            
            </plugins>
            <resources>
                <!-- 标签定义了项目中资源文件的位置和类型 -->
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <!-- 这里的**表示任意多层目录,*.xml表示任意名称的.xml文件。这样一来,这个配置会递归地从src/main/java目录开始,包含所有.xml扩展名的文件。 -->
                        <include>**/*.xml</include>
                    </includes>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                </resource>
            </resources>
        </build>
    
        <!-- 依赖 -->
        <dependencies>
            <!-- 单元测试 -->
            <!-- https://mvnrepository.com/artifact/junit/junit -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
            
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.6</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.22</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.20</version>
                <scope>provided</scope>
            </dependency>
    
        </dependencies>
    </project>
  3. 在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>
        <!-- 配置一个环境,一个环境对应着一个数据库,default为某一个环境的id -->
        <environments default="mysql">
            <environment id="mysql">
                <!-- 事务管理器,jdbc/managed(由外部容器管理,自己什么也不做) -->
                <transactionManager type="jdbc"></transactionManager>
                <!-- 数据源,unpooled、pooled、jndi -->
                <dataSource type="pooled">
                    <property name="url" value="jdbc:mysql://localhost:3306/mydb?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"/>
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
            <package name="com.neu.mapper"></package>
        </mappers>
    </configuration>

使用log4j生成日志

添加依赖

<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
</dependency>

配置,在resource目录下添加文件log4j.properties

# Global logging configuration
#\u751F\u4EA7\u73AF\u5883\u914D\u7F6Einfo   ERROR
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
# log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 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. 使用参数名:arg0、arg1…或param1、param2

    <select id="find" resultType="com.neu.entity.Dept">
            select *
            from dept
            where dname=#{param1} and loc=#{param2};
        </select>
  2. 使用注解@Param(推荐)

    List<Dept> find(@Param("dname") String dname,@Param("loc") String loc);
    <select id="find" resultType="com.neu.entity.Dept">
            select *
            from dept
            where dname=#{dname} and loc=#{loc};
        </select>
  3. 使用实体类封装多个参数

    List<Dept> find2(Dept dept);
    <select id="find2" resultType="com.neu.entity.Dept">
           select *
           from dept
           where dname=#{dname} and loc=#{loc}
       </select>
  4. 使用map集合封装多个参数(很灵活)

    List<Dept> find3(Map map);
    
    
    @Test
        public void find3() {
            Map<String,String> map = new HashMap();
            map.put("dname","hr");
            map.put("loc","sy");
            List<Dept> list = deptMapper.find3(map);
            System.out.println(list);
        }
    <select id="find3" resultType="com.neu.entity.Dept">
            select *
            from dept
            where dname=#{dname} and loc=#{loc}
        </select>

#{}和${}

  1. #{}:使用占位符的方式生成sql语句,sql语句中的列名为变量的时候,不能使用

  2. ${}:使用拼接字符串的方式生成sql语句,参数为列名的时候可以使用

    List<Dept> findByOrder(String columnName);
    <select id="findByOrder" resultType="com.neu.entity.Dept">
        select *
        from dept
        order by ${columnName}
    </select>
  3. 模糊查询

    //第一种
    List<Dept> findByDname(String dname);
    
    //xml
    <select id="findByDname" resultType="com.neu.entity.Dept">
            select *
            from dept
            where dname like #{dname}
        </select>
            
    //test
    @Test
        public void findByDname() {
            String dname = "h";
            List<Dept> name = deptMapper.findByDname("%"+dname+"%");
            System.out.println(name);
        }
    //第二种
    <select id="findByDname" resultType="com.neu.entity.Dept">
            select *
            from dept
            where dname like '%${dname}%'
        </select>
        
    //test
    @Test
        public void findByDname() {
            String dname = "h";
            List<Dept> name = deptMapper.findByDname(dname);
            System.out.println(name);
        }
        

SQL片段

  1. 定义:用来定义可重复使用的sql代码段,可以包含在其他的sql语句中

  2. 使用

    • 定义sql片段

      <sql id="getAll">select * from dept</sql>
    • 引用sql片段

      <include refid="getAll"></include>

得到插入的主键值

<selectKey>

  • order:决定了查询语句执行的先后

    • after:先执行插入语句,后执行查询主键的sql语句(MySQL)
    • before:先执行查询(生成)主键的sql语句,后执行插入语句(Oracle)
  • keyProperty:键的属性值,查询得到的主键值放到形参的那个属性中

  • resultType:查询主键值的类型

  • 使用:

    <insert id="insert">
            <selectKey order="AFTER" keyProperty="deptno" resultType="int">
                select last_insert_id()
            </selectKey>
            insert into dept values (null,#{dname},#{loc})
        </insert>

查询返回简单数据类型

int count();//得到表的记录行数
<select id="count" resultType="java.lang.Integer">
        select count(*)
        from dept
</select>
    
    
String getDname(int deptno);
<select id="getDname" resultType="java.lang.String">
        select dname
        from dept
        where deptno = #{deptno}
</select>

//返回boolean类型,如果查询记录数>0返回true,否则返回false
boolean ifExist(String dname);
<select id="ifExist" resultType="java.lang.Boolean">
        select count(*)
        from dept
        where dname=#{dname}
</select>

动态Sql

  1. <if test="表达式">sql片段</if>
    
    
    当表达式为true的时候,输出SQL片段,否则不输出
    
    ~~~java
    List<Dept> search(Dept dept);
    
    
    //test
    @Test
        public void search() {
            List<Dept> search = deptMapper.search(new Dept(null, "HR", null));
            System.out.println(search);
        }
    //xml
    <select id="search" resultType="com.neu.entity.Dept">
            select *
            from dept
            where 1=1
            <if test="deptno!=null">
                and deptno = #{deptno}
            </if>
    
            <if test="dname!=null">
                and dname=#{dname}
            </if>
    
            <if test="loc!=null">
                and loc=#{loc}
            </if>
    
    
        </select>
  2. <!--使用trim标签-->
    <trim prefix="前缀" prefixOverrides="被覆盖的前缀"></trim>
    
    
    prefix:如果trim标签中不为空串,则添加该前缀,如果内容为空串,则不添加
    
    prefixOverrides:如果trim标签内容最前面的部分是该属性的内容,则覆盖掉
    
    ~~~xml
    <select id="search" resultType="com.neu.entity.Dept">
            select * from dept
            <trim prefix="where" prefixOverrides="and">
                <if test="deptno!=null">
                    and deptno = #{deptno}
                </if>
    
                <if test="dname!=null">
                    and dname=#{dname}
                </if>
    
                <if test="loc!=null">
                    and loc=#{loc}
                </if>
            </trim>
        </select>
  3. <where></where> <!--相当于把第二个打包了-->
    
    
    ~~~xml
    select * from dept
            <where>
                <if test="deptno!=null">
                    and deptno = #{deptno}
                </if>
    
                <if test="dname!=null">
                    and dname=#{dname}
                </if>
    
                <if test="loc!=null">
                    and loc=#{loc}
                </if>
            </where>
  4. <trim suffixOverrides="要覆盖的后缀字符串"></trim>
    
    
    ~~~xml
    <!--变更非空字段-->
    <update id="update2">
            update dept
            <trim prefix="set" suffixOverrides=",">
                <if test="dname!=null">
                    dname=#{dname},
                </if>
                <if test="loc!=null">
                    loc=#{loc},
                </if>
            </trim>
                where deptno = #{deptno}
        </update>
  5. <set></set><!--相当于把第4个打包了-->
    
    
    ~~~xml
    <update id="update2">
            update dept
            <set>
                <if test="dname!=null">
                    dname=#{dname},
                </if>
                <if test="loc!=null">
                    loc=#{loc},
                </if>
            </set>
                where deptno = #{deptno}
        </update>
  6. <foreach></foreach>:用来处理集合和数组
    
    
    属性:
    
    - collection:list|connection|array
    
    - item:引用集合或数组中的当前遍历元素
    
    - separator:分隔符
    
      ~~~xml
      <insert id="batchInsert">
              insert into dept values
              <foreach collection="list" item="dept" separator=",">
                  (#{dept.deptno},#{dept.dname},#{dept.loc})
              </foreach>
          </insert>
    属性: - open:要添加的开始字符串 - close:要添加的结束字符串
    int batchDelete(int[] deptnos);
    <delete id="batchDelete">
            delete from dept where deptno in
            <foreach collection="array" item="deptno" separator="," open="(" close=")">
                #{deptno}
            </foreach>
    
        </delete>

分页

List<Dept> getPaged(@Param("pageNum") int pageNum, @Param("pageSize") int pageSize);
<select id="getPaged" resultType="com.neu.entity.Dept">
        <include refid="selectAll"></include>
        order by deptno
        limit ${(pageNum-1)*pageSize},#{pageSize}
</select>

列名与与属性名不一致

  1. 在SQL语句中为列起别名,别名与属性名一致

  2. 使用<resultMap>来进行映射

    //如果列名和属性名相同,可以自动映射,不用写
    <resultMap id="deptMap" type="com.neu.entity.Dept">
            <result column="dname" property="dname2"></result
        </resultMap>
    
        <select id="getAll" resultMap="deptMap">
            select deptno,dname,loc from dept;
        </select>
    • <id>:用来映射主键
    • <result>:用来映射非主键
    • column:列名
    • properly:实体类的属性名

实体关联

  • <association>:关联(实体),表示一对一关系(emp=>dept)

    • 实体类

      package com.neu.entity;
      
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.util.Date;
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      
      public class Emp {
          private Integer EMPNO;
          private String ENAME;
          private String JOB;
          private Integer MGR;
          private Date HIREDATE;
          private Double SAL;
          private Double COMM;
          private Integer DEPTNO;
          private Dept dept;
      }
    • 方法一:表连接,手工映射

      • <select id="getById" resultMap="empMap">
                select e.* ,d.dname,d.loc
                from emp e join dept d on e.deptno = d.deptno
                where empno = #{empno}
            </select>
        
        <resultMap id="empMap" type="com.neu.entity.Emp">
                <id column="EMPNO" property="EMPNO"></id>
                <result column="ENAME" property="ENAME"></result>
                <result column="JOB" property="JOB"></result>
                <result column="HIREDATE" property="HIREDATE"></result>
                <result column="MGR" property="MGR"></result>
                <result column="SAL" property="SAL"></result>
                <result column="COMM" property="COMM"></result>
                <result column="DEPTNO" property="DEPTNO"></result>
        
                <association property="dept" javaType="com.neu.entity.Dept">
                    <id column="deptno" property="deptno"></id>
                    <result column="dname" property="dname2"></result>
                    <result column="loc" property="loc"></result>
                </association>
            </resultMap>
        
        
        - 方法二:通过方法实现映射(列名到实体的映射)
        
          - ~~~xml
            <resultMap id="empMap" type="com.neu.entity.Emp">
                    <result column="DEPTNO" property="DEPTNO"></result>
                    <association property="dept" column="DEPTNO" select="com.neu.mapper.DeptMapper.getById">
            
                    </association>
                </resultMap>
            
                <select id="getById" resultMap="empMap">
                    select * from emp where empno=#{empno}
                </select>
  • :关联集合,一对多的关系

    package com.neu.entity;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.List;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Dept {
        private Integer deptno;
        private String dname2;
        private String loc;
    	//新增
        private List<Emp> emps;
    }
    
    <resultMap id="deptMap" type="com.neu.entity.Dept">
           <collection property="emps" column="deptno" select="com.neu.mapper.EmpMapper.getByDeptNo">
      
           </collection>
       </resultMap>
       <select id="getById" resultMap="deptMap">
           select * from dept where deptno = #{deptno}
       </select>

使用注解替代mapper文件

  1. 使用注解

    @Select("select * from emp")
        List<Emp> getAll();
    @Delete("delete from emp where empno = #{empno}")
        int delete(int empno);
  2. 长SQL语句的写法

    //拼串
    @Update("update emp set sal = #{sal}"+
    		" where empno = #{empno}")
    int update(Emp emp)
        
    //数组
    @Update({"update emp set sal = #{sal}",
    		" where empno = #{empno}"})
    int update(Emp emp)
  3. ResultMap(mybatis 3.5.4)

    @Select("select * from emp")
       List<Emp> getAll();
       @Select("select * from emp where empno=#{empno}")
       @Results(id = "map2",value={
               @Result(column = "empno",property = "EMPNO",id = true),
               @Result(column = "ename",property = "ENAME"),
               @Result(column = "job",property = "JOB"),
               @Result(property = "dept",column = "deptno" ,many = @Many(select = "com.neu.mapper.DeptMapper.getById"))
       })
       Emp getById(int empno);

延迟加载

  1. 定义:在数据与对象进行mapping的映射操作时,只有在真正使用该对象的时候才进行mapping操作,以减少数据库开销,从而提高系统性能

  2. 默认情况下mybatis没有开启延迟加载功能,如果要开启,需要配置SqlMapConfig.xml

    <settings>
            <!--开始延迟加载-->
            <setting name="lazyLoadingEnabled" value="true"/>
            <!--取消积极加载-->
            <setting name="aggressiveLazyLoading" value="false"/>
        </settings>

缓存

  1. 定义:是一种以空间换时间设计理念,利用内存或硬盘空间来提高数据检索速度的有效手段之一

  2. 一级缓存:默认开启,把数据缓存到当前会话中(SqlSession)

  3. 二级缓存:默认没有开启,需要手工开启,修改SqlMapConfig.xml文件

    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
    • 在需要缓存的mapper文件中设置

      <cache></cache>
    • 注意:二级缓存对象要实现可序列化接口(Serializable)

  4. 注意:使用缓存时,需要考虑缓存数据的时效性和一致性