Mybatis代码记录

初始化Mybatis项目

新建maven工程,导入依赖

 <dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

IDEA中database的设置

image-20220217135454390

image-20220217135707395

image-20220217135916774

image-20220217140006425

image-20220217140129810

小tips

可以直接在idea中的控制台输入sql指令,非常方便

image-20220217140212776

image-20220217140334010

generate …可以生成实体类
go to scripts Directory可以配置生成实体类的模板,下面是我的部分配置,只需要改一部分就行了

image-20220217140555825

附上修改的代码

//修改包名
packageName = "com.lingxi.pojo;"
.
.
.
def generate(out, className, fields) {
  out.println "package $packageName"
  out.println ""
  //导入lombok包、把mysql的时间改为date
  out.println "import lombok.Data;"
  out.println "import java.util.Date;"
  //加入注解
  out.println "@Data"
  out.println "public class $className {"
  out.println ""
  fields.each() {
    if (it.annos != "") out.println "  ${it.annos}"
    out.println "  private ${it.type} ${it.name};"
  }
  out.println ""
  //注释原来的set和get
/*  fields.each() {
    out.println ""
    out.println "  public $

生成效果

image-20220217141430665

alt+回车解决问题,自动导包到项目路径下

备注,还要在idea的setting插件中心下载个插件神器,方便在xml和接口中跳转

原(下架了):easy mybatis
改:mybatisx:可以快速在xml和接口跳转,生成xml配置文件

照猫画虎,模仿官网

基本配置

学mybatis一定要看官网

第一步,配置mybatis.config

这里暂时先直接写死,后面有更优方案

<?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.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/4032?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
  <!--这里是可以放很多东西的,预留-->
</configuration>

测试类,以下是通用部分

//@BeforeClass @Before   @After  @AfterClass

    private static SqlSessionFactory sqlSessionFactory = null;
    private static InputStream inputStream = null;

    private SqlSession sqlSession = null;

    /**
     * 在开始创建类的时候运行这个方法
     */
    @BeforeClass
    public static void init() {
        //1.加载mybatis 配置文件:核心 和 映射文件
        String resource = "mybatis-config.xml";


        //读取配置文件(注意导入正确的包): 方式一:
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //2.创建sqlSessionFactory(读取流)
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    /**
     * 在完成这个类的时候调用方法
     */
    @AfterClass
    public static void destroy() {

        sqlSessionFactory = null;
        //6.关闭流
        if (null != inputStream) {
            try {
                inputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 在开始方法前调用此方法
     */
    @Before
    public void openSession() {
        //3.创建sqlSession
        sqlSession = sqlSessionFactory.openSession();
    }

    /**
     * 在调用方法后调用此方法
     */
    @After
    public void closeSqlSession() {
        //5.关闭sqlSession
        if (null != sqlSession) {

            sqlSession.commit();
            //关闭sqlSession
            sqlSession.close();
        }

    }

开始准备Mapper的接口和xml配置文件

建议先建立最简单的xml和接口,因为有mybaits插件的原因,可以节省很多工作量

Mapper.xml模板

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lingxi.mapper.SysuserMapper">
    <!-- namespace :代表一个映射文件,可以随便写,
    但是我们实际开发中我们写 Mapper接口的权限定类名
    -->
    <!-- id: 唯一标识 ,id和namespace 联合起来可以定位到具体的sql语句 -->

</mapper>
public interface SysuserMapper {
  
}

这时候写好接口方法,插件会提醒

image-20220217150836716

写sql完事

image-20220217150900734

不自信的话可以先在database的控制台里面敲一下试试

写好sql、接口方法、测试方法后,别忘了在mybaits-config里面注册mapper

<mappers>
    <mapper resource="com/lingxi/mapper/SysuserMapper.xml"/>
</mappers>

CRUD(XML配置实现)

个人认为编写顺序

接口方法 → xml配置写sql → 测试方法

核心代码太简单,而且要复制几个文件,懒得写了
但是提一个点,sqlsession执行方法的两种方法(推荐第二种)

//        通过完全限定名+方法名实现
        List<Sysuser> sysusers = sqlSession.selectList("com.lingxi.mapper.SysuserMapper.selectAllUser");
//        通过调用接口实现
  /*      SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
        List<Sysuser> sysusers = mapper.selectAllUser();*/
//  mapper是sqlSession生成的动态代理对象

CRUD(注解实现,推荐使用简单的)

@Select("select * from sysrole")
List<Sysrole> getAllrole();

@Insert("insert into sysrole (rname,rdec,create_time,update_time) values (#{rname},#{rdec},#{createTime},#{updateTime})")
int addRole(Sysrole sysrole);

@Delete("delete from sysrole where rid=#{id}")
int delRole(int id);

@Update("update sysrole set rname=#{rname},rdec=#{rdec},create_time=#{createTime},update_time=#{updateTime} where rid=#{rid}")
int updateRole(Sysrole sysrole);

@Select("select * from sysrole where rid=#{id}")
Sysrole selectOneByRid(int id);

增强功能

新增用户,返回自增ID

废话少说,直接上代码

接口

    /**
     * 新增用户,并且拿到返回的自增主键,返回到传过来的user中
     * @param sysuser
     * @return
     */
    int addUserAndGetKey(Sysuser sysuser);

xml配置

<insert id="addUserAndGetKey" keyProperty="uid" useGeneratedKeys="true">
    insert into sysuser (uname, upwd, uemail, phone, create_time, update_time, deleted, rid) values (#{uname},#{ upwd},#{ uemail},#{ phone},#{ createTime},#{ updateTime},#{ deleted},#{ rid});
</insert>

测试方法

  @Test
    public void test_addUserAndGetKey() {
        SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
        Sysuser sysuser = new Sysuser();
        sysuser.setUname("自增用户");
        sysuser.setUpwd("123");
        int i = mapper.addUserAndGetKey(sysuser);
        System.out.println("影响行数"+i);
        System.out.println("自增主键"+sysuser.getUid());
    }

结果

image-20220217163013912

模糊查询

<select id="selectLikeUser" resultType="com.lingxi.pojo.Sysuser">
    select  * from  sysuser where uname like '%' #{uname} '%'; 
</select>

注意

  1. '%' #{uname} '%'之间是有空格的

  2. /**/用在sql之间会报错,是那种sql语句就不能在里面报错,之前不信,现在信了
    image-20220217165221530

  3. 可以使用双引号,但不建议

多参数查询

使用注释和默认多参数名称

接口

 List<Sysuser> selectUserByMultiParams(@Param("name") String name, @Param("pwd")String pwd);

xml

<select id="selectUserByMultiParams" resultType="com.lingxi.pojo.Sysuser">
    select  * from  sysuser where uname like '%${name}%' and upwd=#{pwd};
</select>

或者不使用@param

使用arg1, arg0, param1, param2
<select id="selectUserByMultiParams" resultType="com.lingxi.pojo.Sysuser">
    select  * from  sysuser where uname like '%${param1}%' and upwd=#{param2};
</select>

使用Map

接口

 List<Sysuser> selectUserByMap(Map map);

xml

    <select id="selectUserByMap" resultType="com.lingxi.pojo.Sysuser">
         select  * from  sysuser where uname like '%${name}%' and upwd=#{pwd};
    </select>

测试方法

 @Test
public void test_selectUserByMap() {
    SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
     HashMap<String, String> map = new HashMap<>();
     map.put("name", "张");
     map.put("pwd", "123456");
     List<Sysuser> sysuserList = mapper.selectUserByMap(map);
     sysuserList.forEach(System.out::println);
 }

使用自定义实体类

极其lowB,不建议使用,这个原理就和表传对应的实体类是一个原理

配置日志

加入依赖

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

配置文件log4j.properties

### \u8BBE\u7F6E###
log4j.rootLogger = debug,stdout,D,E
### \u8F93\u51FA\u4FE1\u606F\u5230\u63A7\u5236\u62AC ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### \u8F93\u51FADEBUG \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = D://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### \u8F93\u51FAERROR \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =D://logs/error.log 
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

配置数据库properties

先写好properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/4032?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC
jdbc.username=root
jdbc.password=root

为什么要写jdbc前缀呢?防止冲突
另外注意properties不需要转义字符

mybatis的配置文件需要改两个地方

加上properties的标签,注意这个是有顺序的

    <properties resource="db.properties"/>

改好环境配置

  <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>

驼峰命名

<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

设置别名

<!--设置别名-->
<typeAliases>
    <!--单个实体类设置别名-->
    <typeAlias type="com.lingxi.pojo.Sysuser" alias="User"/>
    <!--包扫描方法,全局配置,默认别名为类名-->
    <package name="com.lingxi.pojo"/>
</typeAliases>

设置Mapper映射

<mappers>
    <!--第一种方式:单独配置xml文件-->
    <mapper resource="com/lingxi/mapper/SysuserMapper.xml"/>
    <!--第二种方式:单独配置类名-->
    <mapper class="com.lingxi.mapper.SysroleMapper"/>
    <!--第三种方式:全局配置,包扫描-->
    <package name="com.lingxi.mapper"/>
</mappers>

复杂功能

一对一(对象中包含对象)

其实重点是在xml中的配置,先介绍一下resultMap的概念,resultMap实际上可以解决数据库字段名与实体类不对应的问题

  <select id="selectUserBySort" resultMap="sortMap">
        select * from sysuser order by ${sortedColumn} ${sorted};
    </select>
  <!--resultMap可以解决数据库中字段与实体类的属性不一致的问题
	resultMap的id要对应
    resultMap和resultType只能配置一个
    -->
    <resultMap id="sortMap" type="Sysuser">
        <!--id必须配置,其他与实体类一致的可以不配置-->
        <id column="uid" property="uid"/>
        <result column="create_time" property="createTime"/>
    </resultMap>

那么,就是将resultMap的功能再次升级

<select id="getUserByIdContainRole" resultMap="UserContainRole">
    select  * from sysuser where uid=#{id}
</select>
<resultMap id="UserContainRole" type="Sysuser">
    <id property="uid" column="uid"/>
    <association property="sysrole" javaType="com.lingxi.pojo.Sysrole"
                 select="com.lingxi.mapper.SysroleMapper.selectOneByRid" column="rid"/>
</resultMap>

这其中有几点我要解释一下

  1. resultMap中id是必须要配置的,实体类中的id要与数据库中表的id对应
  2. association代表这个列的联系
  3. javaType表示这个联系会最后转成java的什么类型
  4. select表示这个列后续的查找操作,称之为子查询,这里是引用了一个方法
  5. column是表示将这个列作为需要的参数传入select指向的方法之中

一对多(一个实体类内包含集合)

方法一:利用sql进行查询,需要处理列

xml配置

<select id="getRoleContainUser" resultMap="roleContainUser">
      select sr.* ,su.uid,su.uname,su.upwd,su.uemail,su.phone,su.deleted,su.rid su_rid,su.create_time su_create_time,su.update_time su_update_time
      from sysrole sr ,sysuser su
      where sr.rid = su.rid and sr.rid = #{rid}
    </select>
    <resultMap id="roleContainUser" type="com.lingxi.pojo.Sysrole">
        <id column="rid" property="rid"></id>
        <result column="rname" property="rname"></result>
        <result column="create_time" property="createTime"></result>
        <result column="update_time" property="updateTime"></result>
        <result column="rdec" property="rdec"></result>
        <collection property="sysusers" ofType="com.lingxi.pojo.Sysuser">
            <id column="uid" property="uid"></id>
            <result column="uname" property="uname"></result>
            <result column="upwd" property="upwd"></result>
            <result column="uemail" property="uemail"></result>
            <result column="create_time" property="createTime"></result>
            <result column="update_time" property="updateTime"></result>
            <result column="phone" property="phone"></result>
            <result column="deleted" property="deleted"></result>
            <result column="su_rid" property="rid"></result>
        </collection>
    </resultMap>

解释:

这个方法其实是直接通过sql进行查询,将查询的结果直接每个列都映射到相应实体类中,下面来解释标签的作用

  1. collection指的是集合,也就是这个标签返回回来的类型必然是个集合,因为是collection,所以里面可以有多个result标签,对应着相应实体类的值
  2. ofType表示它是一个实体类中的实体类
  3. property对应的sysusers就是List,所以直接就映射回去了
  4. result就是普通的值,就是普通的列对应对象或者基本类型这种

方法二:利用子查询

xml配置

  <select id="getRoleContainUserBy2" resultMap="roleContainUser2">
        select * from sysrole where rid=#{rid}
    </select>
    <resultMap id="roleContainUser2" type="com.lingxi.pojo.Sysrole">
        <id column="rid" property="rid"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
        <!--这里有两种写法-->
        <!--第一种,建立联系-->
        <association property="sysusers" javaType="arraylist" column="rid" select="com.lingxi.mapper.SysuserMapper.getUserByRid"/>
        <!--第二种,建立集合关系-->
        <collection property="sysusers" ofType="com.lingxi.pojo.Sysuser" select="com.lingxi.mapper.SysuserMapper.getUserByRid" column="rid"/>
    </resultMap>
<select id="getUserByRid" resultType="com.lingxi.pojo.Sysuser">
    select *from sysuser where rid=#{rid};
</select>

解释:其实感觉没啥好解释的,但是可以说一下流程

  1. 找到需要的信息
  2. 根据信息中的关键字段,继续查找
  3. 将关键字段查找到的信息再次映射回类型中

懒加载(了解)

mybaits核心配置xml添加setting

<setting name="lazyLoadingEnabled" value="true"></setting>

image-20220217221543070

image-20220217221747749

小记:

生效场景:mybatis中的延迟加载仅在有二次查询的场景下生效.
延迟加载目的:减少无效的数据库查询操作,提高响应的效率。
延迟加载应用:分页,树形结构数据,前端图片的延迟加载

除了toString 以外,只要是没有使用到sysrole ,就不会触发二次查询

缓存

cache:减少数据库的重复查询操作.基于内存把第一次查询的结果保存的缓存对象中,当后续查询时,先查询缓存,缓存中有则返回;缓存中没有则查询数据库,并把查询结果放缓存。(核心,写代码的逻辑)
ehcache 占用web服务器的内存(一般不用)

memercache,redis 独立部署的缓存

mybatis的缓存分为两级:
session级别缓存(一级缓存):默认开启 同一个sqlSession
factory级别缓存(二级缓存):默认未开启. 同一个sqlSessionFactory

一级缓存:

在web系统中,每次用户请求,后端开启sqlsession,查询完毕,session关闭.
默认开启,只在一次sqlsession中生效

image-20220217234007159

image-20220217234105110

二级缓存:

不同的sqlsession进行操作,可以命中缓存

核心配置文件添加设置:

<setting name="cacheEnabled" value="true"></setting>

映射配置文件:

缓存策略

 <!--只对当前namespace下的所有查询全部生效,-->
<cache
        eviction="FIFO"
        flushInterval="60000"
        size="512"
        readOnly="true"/>
<!--可以再select中加usecache禁用二级缓存-->
<select id="selectOneById" resultType="com.lingxi.pojo.Sysuser" useCache="false">
        select  * from sysuser where uid=#{id}
    </select>
<!--可以在update,delete中使用flushcache强制清空缓存
清空原因:数据变化了
-->
  <update id="updateUser" flushCache="true">
        update sysuser set uname = #{uname},upwd = #{upwd}where uid=#{uid};
    </update>
  1. FIFO:先进先出
  2. 缓存大小:512个引用
  3. 缓存间隔:60000毫秒,一分钟刷新一次
  4. 只读

image-20220217235900145

失去一级缓存时会查询两次,且日志也不会报缓存命中率,这个懒得截图了,随便改个配置都会导致二级缓存失效

sql标签

开发中一般是不使用*作为查询所有列的,所以推荐使用sql标签,需要在mapper的配置文件中添加如下

<sql id="allColumn">
        uid , uname, upwd, uemail, phone, create_time, update_time, deleted, rid
    </sql>

然后再查询标签中引用

<select id="selectAllUser" resultType="com.lingxi.pojo.Sysuser">
        select
        <include refid="allColumn"/>
        from sysuser;
    </select>

查询结果

image-20220218085710463

tips:idea中如果配置了sql方言可能会报错,修改如下

img

动态sql

动态的生成SQL语句,防止条件不足和减轻代码量

where标签

其实就是把where标签里面的第一个and的标签去掉,其实不用where标签也可以,使用where1=1也可以但是不符合规范

<select id="getUserWhere" resultType="com.lingxi.pojo.Sysuser">
        select
        <include refid="allColumn"/>
        from sysuser
      <where>
          <if test="uname!=null and uname!=''">
             and uname like '%' #{uname} '%'
          </if>
          <if test="upwd!=null and upwd!=''">
             and upwd = '%' #{upwd} '%'
          </if>
      </where>

set标签

更新的字段有些不更新,就可以去掉逗号,起到只修改指定字段的效果

 <update id="updateUser" flushCache="true">
        update sysuser
        <set>
            <if test="uname!=null and uname!=''">
                ,uname = #{uname}
            </if>
            <if test="upwd!=null and upwd!=''">
                ,upwd = #{upwd}
            </if>
        </set>
           where uid = #{uid};
    </update>

foreach标签

在传入多个数值时,用于拼接sql

<delete id="delUsers">
    delete from sysuser
    where uid in
    <foreach collection="list" item="uid" separator="," open="(" close=")">
        #{uid}
    </foreach>
</delete>

foreach内部解释:

  1. collection支持collection(list)和array
  2. item就是代指内部的项目的每个名字,我感觉有点类似于java中的for增强即[for(item in items)]
  3. 也可以通过@Param的注释在collection中传递自定义名称

批量插入用户,foreach配合对象使用

 <insert id="batchAddUsers">
        insert into sysuser (uname,upwd) values
        <foreach collection="list" separator="," item="user" >
            (#{user.uname},#{user.upwd} )
        </foreach>
    </insert>

注意这里不要加open和close,语法问题,貌似mybaits会自己添加( )

posted @ 2022-02-18 11:24  凌曦的博客  阅读(47)  评论(0)    收藏  举报