专栏首页技术进阶之路从零开始重新认识 MyBatis

从零开始重新认识 MyBatis

第一次接触MyBatis框架还是在半年前,当时第一次用的时候觉得真爽啊,不用自己封装工具类了,也不用自己管理数据库连接了,而且性能也提高了不少。但是后来在开发中逐渐接触到更多的框架之后,比如 MyBatisPlus 以及 JPA 等,发现这些框架更牛,省去了最基本的CRUD,于是就去使用那些更能提高开发效率的框架了,到现在前前后后也有半年没碰这个框架了,很是惭愧,现在忘得一干二净了,今天花了一天的时间回顾了一下 MyBatis 的基本操作,以及底层原理,被该框架所使用到的设计模式以及思想所折服。太强了,每看到一处都直呼 妙啊

最近在回顾 java 关于数据库的操作,有以下文章供参考:

  • JavaWeb 使用 Druid 连接池查询数据库
  • apache-commons-dbutils + Druid + JDBC 简单实现 CRUD

一、环境搭建

首先我们先把环境搭建好,具体的就是以下几个配置文件:

  • pom.xml: maven 的项目对象映射文件,它将一个项目看成一个对象,这里是为了导入包。
  • SqlMapConfig.xml: Mybatis 的主配置文件,用于注册其他的 Mapper,以及建立数据库连接。
  • jdbcConfig.properties: jdbc 连接所必须的配置信息,这里我们单独的抽取出来作为一个文件。
  • log4j.properties: 用于记录日志信息。

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>top.wsuo</groupId>
    <artifactId>wsuo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
        </dependency>
    </dependencies>
</project>

SqlMapConfig.xml:

这里 MyBatis 提供多种配置方式,这里我们都采取了最优的方式配置了,注释掉的为其他配置方式。

<?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>
    <!--  可以引用外部配置文件  -->
    <properties resource="jdbcConfig.properties"/>
    <typeAliases>
<!--        <typeAlias type="top.wsuo.pojo.User" alias="user"/>-->
<!--        自动识别包下面的类名作为别名 -->
        <package name="top.wsuo.pojo"/>
    </typeAliases>
    <environments default="mysql">
        <environment id="mysql">
            <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>
    <mappers>
<!--        <mapper resource="mapper/IUserDao.xml"/>-->
        <package name="top.wsuo.dao"/>
    </mappers>
</configuration>

jdbcConfig.properties:

注意后面加上编码规则,不然插入操作有可能会出现乱码问题。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///eesy?useUnicode=true&amp;characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root

log4j.properties:

这是 log4j 的配置文件,具体如何运行的可以不用管,他会在项目运行的时候在根目录生成一个axis.log文件,用于记录你在控制台所有的输出信息。

# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

然后我们先将数据库中的表建好:

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user`
(
    `id`       int(11)     NOT NULL auto_increment,
    `username` varchar(32) NOT NULL COMMENT '用户名称',
    `birthday` datetime     default NULL COMMENT '生日',
    `sex`      char(1)      default NULL COMMENT '性别',
    `address`  varchar(256) default NULL COMMENT '地址',
    PRIMARY KEY (`id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;



insert into `user`(`id`, `username`, `birthday`, `sex`, `address`)
values (41, '老王', '2018-02-27 17:47:08', '男', '北京'),
       (42, '小二王', '2018-03-02 15:09:37', '女', '北京金燕龙'),
       (43, '小二王', '2018-03-04 11:34:34', '女', '北京金燕龙'),
       (46, '老王', '2018-03-07 17:37:26', '男', '北京'),
       (48, '小马宝莉', '2018-03-08 11:44:00', '女', '北京修正');



DROP TABLE IF EXISTS `account`;

CREATE TABLE `account`
(
    `ID`    int(11) NOT NULL COMMENT '编号',
    `UID`   int(11) default NULL COMMENT '用户编号',
    `MONEY` double  default NULL COMMENT '金额',
    PRIMARY KEY (`ID`),
    KEY `FK_Reference_8` (`UID`),
    CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;



insert into `account`(`ID`, `UID`, `MONEY`)
values (1, 41, 1000),
       (2, 45, 1000),
       (3, 41, 2000);



DROP TABLE IF EXISTS `role`;

CREATE TABLE `role`
(
    `ID`        int(11) NOT NULL COMMENT '编号',
    `ROLE_NAME` varchar(30) default NULL COMMENT '角色名称',
    `ROLE_DESC` varchar(60) default NULL COMMENT '角色描述',
    PRIMARY KEY (`ID`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;



insert into `role`(`ID`, `ROLE_NAME`, `ROLE_DESC`)
values (1, '院长', '管理整个学院'),
       (2, '总裁', '管理整个公司'),
       (3, '校长', '管理整个学校');



DROP TABLE IF EXISTS `user_role`;

CREATE TABLE `user_role`
(
    `UID` int(11) NOT NULL COMMENT '用户编号',
    `RID` int(11) NOT NULL COMMENT '角色编号',
    PRIMARY KEY (`UID`, `RID`),
    KEY `FK_Reference_10` (`RID`),
    CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
    CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

insert into `user_role`(`UID`, `RID`)
values (41, 1),
       (45, 1),
       (41, 2);

总共有四张表:

  • 用户表 User
  • 账单表 Account
  • 角色表 Role
  • 用户角色中间表 user_role

二、单表操作

实体类

单表操作这里以 User 为例。

首先我们创建出实体类 User,省略了 get set 方法:

package top.wsuo.pojo;

import java.io.Serializable;
import java.util.Date;

/**
 * 用户
 *
 * @Author shuo wang
 * @Date 2020/5/24 0024 6:16
 * @Version 1.0
 */
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}

Dao 接口

然后是 IUserDao 这个接口:

package top.wsuo.dao;

import top.wsuo.pojo.User;

import java.util.List;

/**
 * 账户接口
 *
 * @author shuo wang
 * @version 1.0
 * @date 2020/5/24 0024 6:16
 */
public interface IUserDao {

    /**
     * 查询所有
     *
     * @return 返回的是list集合
     */
    List<User> findAll();

    /**
     * 保存用户
     *
     * @param user 需要一个用户对象
     */
    void saveUser(User user);

    /**
     * 修改记录
     *
     * @param user 需要一个用户对象
     */
    void updateUser(User user);

    /**
     * 删除用户
     *
     * @param id 需要用户的 id
     */
    void deleteUser(Integer id);

    /**
     * 根据id查询
     *
     * @param id 待查用户的 id
     * @return 但会用户对象
     */
    User findById(Integer id);

    /**
     * 模糊查询
     *
     * @param username 用户名
     * @return 返回所有满足条件的用户
     */
    List<User> findByUserName(String username);

    /**
     * 动态SQL查询
     *
     * @param user 用户对象
     * @return 返回所有满足条件的 list 集合
     */
    List<User> findUserByCondition(User user);
}

配置文件

但是我们不用去实现这个接口,交给 MyBatis 来实现,所以我们需要通过配置文件来告诉它怎么配置,新建IUserDao.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">
<!--namespace表示这个文件要实现的接口-->
<mapper namespace="top.wsuo.dao.IUserDao">
<!--  查询所有  -->
    <select id="findAll" resultType="user">
        select *
        from user;
    </select>
<!--  保存操作  -->
    <insert id="saveUser" parameterType="user">
        # 查询最后一个插入的id值
        <selectKey resultType="int" keyProperty="id" keyColumn="id" order="AFTER">
            select last_insert_id();
        </selectKey>
        insert into user(username, birthday, sex, address)
        values (#{username}, #{birthday}, #{sex}, #{address})
    </insert>
<!--  更新操作  -->
    <update id="updateUser" parameterType="user">
        update user
        set username=#{username},
            birthday=#{birthday},
            sex=#{sex},
            address=#{address}
        where id = #{id}
    </update>
<!--  删除操作  -->
    <delete id="deleteUser" parameterType="int">
        delete
        from user
        where id = #{id}
    </delete>
<!--  根据 ID 查询  -->
    <select id="findById" parameterType="int" resultType="user">
        select *
        from user
        where id = #{id};
    </select>
<!--  模糊查询  -->
    <select id="findByUserName" parameterType="String" resultType="user">
        select *
        from user
        where username like #{username};
    </select>
<!--  动态SQL  -->
    <select id="findUserByCondition" resultType="list" parameterType="user">
        select *
        from user
        <where>
            <if test="username != null">
                and username = #{username}
            </if>
            ;
        </where>
    </select>
</mapper>

动态 SQL 语句

注意这里使用的:

<where>
    <if test="username != null">
        and username = #{username}
    </if>
    ;
</where>

是动态 SQL 语句。

测试

最后使用 JUnit 创建测试类测试:

public class DaoTest {

    private InputStream in = null;
    private SqlSession sqlSession = null;
    private IUserDao userDao;

    @Before
    public void init() throws IOException {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        sqlSession = new SqlSessionFactoryBuilder()
                .build(in)
                .openSession(true);
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After
    public void destroy() throws IOException {
        sqlSession.close();
        in.close();
    }

    @Test
    public void IUserDaoTest() {
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
        sqlSession.close();
    }

    @Test
    public void TestSave() {
        User user = new User();
        user.setUsername("小李子");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("江苏徐州");
        userDao.saveUser(user);
    }

    @Test
    public void TestUpdate() {
        User user = new User();
        user.setId(49);
        user.setUsername("小李子");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("江苏徐州");
        userDao.updateUser(user);
    }

    @Test
    public void TestDelete() {
        userDao.deleteUser(50);
    }

    @Test
    public void TestFindOne() {
        System.out.println(userDao.findById(49));
    }

    /**
     * 模糊查询,拼接 % % 作为条件
     */
    @Test
    public void TestLike() {
        List<User> users = userDao.findByUserName("%说%");
        for (User user : users) {
            System.out.println(user);
        }
    }

    @Test
    public void TestFindCondition() {
        User user = new User();
        user.setUsername("小说子");
        List<User> users = userDao.findUserByCondition(user);
        for (User u : users) {
            System.out.println(u);
        }
    }

}

三、一对多查询

一对多查询也包括一对一查询。

我们先来看一对一查询,这里体现在每个账单都对应一个用户,我们首先创建一个账单类 Account

User 和 Account 的关系如下:

这里省略了 get set 方法:

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
}

我们想要的结果集应该是这样的:

我们可以直接在 Account 的 xml 配置文件中设置resultMap,让他的输出为一个新的对象映射关系,这里要使用association标签。

<resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>
        <association property="user" javaType="user" column="uid">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="address" column="address"/>
            <result property="sex" column="sex"/>
            <result property="birthday" column="birthday"/>
    	</association>
</resultMap>
<select id="findAll" resultMap="accountUserMap">
        # select *
        # from account;
        select u.*, a.ID as aid, a.uid, a.MONEY
        from account a,
             user u
        where a.UID = u.id;
</select>

注意一点就是这里的 user 要作为 Account 类的成员变量。

// 一对一, Account 中包含 User
private User user;

还有一种方法就是新建一个类,包括以上结果集的每一列,这样也可以实现。

然后是一对多,这里体现在每个用户可以有多个订单,所以我们可以使用 User 去左外连接 Account 。

select *
from user u
left join account a on u.id = a.UID;

我们可以在 IUserDao 中添加一个 resultMap

涉及到一对多的时候需要使用collection标签来表示。

<mapper namespace="top.wsuo.dao.IUserDao">
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="accounts" ofType="account">
            <id property="id" column="id"/>
            <result property="uid" column="uid"/>
            <result property="money" column="money"/>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="userAccountMap">
        select *
        from user u
                 left join account a on u.id = a.UID;
    </select>
    <select id="findById" resultType="user">
        select *
        from user
        where id = #{id};
    </select>
</mapper>

注意这个时候,User 的实体类中应该包含一个 List<Account>,这样才能体现一对多的关系。

// 一对多,User 中包含 Account 的集合
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

我们在写 resultMap 的时候要注意column属性要和查出来的列名保持一致,property属性要和实体类中的相关字段保持一致。

四、多对多查询

最后是多对多,这里我们引入一个中间表。

user 表:

role 表:

role_user 表:

我们要做的查询应该是这样的:

select u.*, r.ID as rid, r.ROLE_NAME, r.ROLE_DESC
from role r
left join user_role ur on r.ID = ur.RID
left join user u on ur.UID = u.id;

我们在 xml 中做如下配置:

<mapper namespace="top.wsuo.dao.IRoleDao">
    <resultMap id="roleMap" type="role">
        <id property="id" column="rid"/>
        <result property="roleName" column="ROLE_NAME"/>
        <result property="roleDesc" column="ROLE_DESC"/>
        <collection property="users" ofType="user">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="birthday" column="birthday"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="roleMap">
        select u.*, r.ID as rid, r.ROLE_NAME, r.ROLE_DESC
        from role r
                 left join user_role ur on r.ID = ur.RID
                 left join user u on ur.UID = u.id;
    </select>
</mapper>

对应的实体类关系为:

public class Role implements Serializable {
    private Integer id;
    private String roleName;
    private String roleDesc;

    // 多对多的关系映射
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }
    //...
}

我们只需要保证自己封装的resultMap 可以对应上查询的结果即可。

总结一下就是对于单表查询,我们无需配置 resultMap ,对于多表查询,一般使用 resultMap 与其子标签 associationcollection 配合使用。

五、延时加载

  • 延时加载常用在一对多多对多中,因为后面是多,有时候我们不需要全部都查出来,都查出来反而浪费时间,所以延时加载。
  • 还有一种是立即加载,常用于一对一多对一中。

下面我们以用户 User 与账单 Account 的一对多关系演示一下延时加载:

<resultMap id="userAccountMap" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="accounts" ofType="account"
                    column="id" select="top.wsuo.dao.IAccountDao.findAccountByUid"/>
</resultMap>

// 注意这里的 findAll 就不能再使用外连接了
<select id="findAll" resultMap="userAccountMap">
        select *
        from user;
</select>
  • column:用用户的 ID 去查所有的账单,所以是 id
  • select:表示用什么方法去查,这里调用的是 Account 中的根据用户 id 查询所有账单的方法;

所以在 Account 的 dao 中应该提供一个方法来获取所有符合条件的 Account:

<select id="findAccountByUid" resultType="account" parameterType="int">
        select *
        from account
        where UID = #{id};
</select>

二级缓存

  • 主配置文件中默认是开启的,所以不用配置;
  • dao 配置文件中添加<cache/>标签;
  • 在执行的方法标签中添加一个属性useCache="true"

六、注解实现

由于注解实现比较简单,所以这里只是用上面的例子简单介绍一下。

注解的单表操作

public interface IUserDao {
    /**
     * 查询所有用户
     *
     * @return 返回用户集合
     */
    @Select("select * from user")
    List<User> findAll();

    /**
     * 保存用户
     */
    @Insert("insert into user(username, address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday})")
    void saveUser(User user);

    /**
     * 更新用户
     *
     * @param user 传入用户对象
     */
    @Update("update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id}")
    void updateUser(User user);

    /**
     * 删除用户
     *
     * @param id 传入用户id
     */
    @Delete("delete from user where id=#{id}")
    void deleteUser(Integer id);

    /**
     * 根据id查询
     *
     * @param id 传入 id
     * @return 返回用户
     */
    @Select("select * from user where id=#{id};")
    User findById(Integer id);

    /**
     * 根据用户名模糊查询
     *
     * @return 返回所有符合条件的用户
     */
    @Select("select * from user where username like #{username}")
    List<User> findByUser();

    /**
     * 查询总共的个数
     *
     * @return 返回个数
     */
    @Select("select count(id) from user;")
    int findTotal();
}

可以看到很简单,不用重新配置方法名和返回值信息,直接在上面写即可,所以单表查起来是很方便的,所以开发中常用注解查单表,使用配置文件查多表。

注解的多表查询

我们之前遇到过数据库表中的字段和实体类字段不对应的时候使用的是 resultMap 标签来配置的,使用注解也可以这样配置,要使用Results(定义) 和 ResultMap(引用) 注解。

可以使用 @one@many 注解实现功能,注解中的属性值和标签的属性值类似。只不过使用的是延迟加载的那种写法,需要从表提供一个根据 id 查询的方法,然后放到 select 属性值中,要使用全限定方法名。

下面我们以一个具体的例子来看一下,还是使用 User 表和 Account 表:

Account 中的代码:

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    //多对一(mybatis中称之为一对一)的映射:一个账户只能属于一个用户
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    
    ······
}

我们之前使用的是 association 标签,现在我么使用注解实现:

/**
* 查询所有账户,并且获取每个账户所属的用户信息
*
* @return 返回 List 集合
*/
@Select("select * from account")
@Results(id = "accountMap", value = {
       @Result(id = true, column = "id", property = "id"),
       @Result(column = "uid", property = "uid"),
       @Result(column = "money", property = "money"),
       @Result(property = "user",
               column = "uid",
               one = @One(select = "top.wsuo.dao.IUserDao.findById", fetchType = FetchType.EAGER))
})
List<Account> findAll();
  • Results:封装结果集,并指定唯一 id
  • Result:指定属性与数据库中属性的映射关系, id 指定是否是主键,默认是 false
  • @One:代表一对一的关系;
  • fetchType = FetchType.EAGER: 立即加载;
  • select:指定全限定类名+方法名。

可以看到使用的是延迟加载的方法,所以我们在 IUserDao 中需要提供一个方法。

/**
 * 根据id查询用户
 *
 * @param userId 用户 ID
 * @return 返回用户对象
 */
@Select("select * from user  where id=#{id} ")
@ResultMap("userMap")
User findById(Integer userId);

注意这里有一个 @ResultMap("userMap"),这是干什么的?我们下面再说。

这样我们就先实现了一对一查询。


那么一对多呢? 肯定是使用 Many 注解了。

/**
 * 查询所有用户
 *
 * @return 返回用户集合
 */
@Select("select * from user")
@Results(id = "userMap", value = {
        @Result(id = true, column = "id", property = "userId"),
        @Result(column = "username", property = "userName"),
        @Result(column = "address", property = "userAddress"),
        @Result(column = "sex", property = "userSex"),
        @Result(column = "birthday", property = "userBirthday"),
        @Result(property = "accounts", column = "id",
                many = @Many(select = "top.wsuo.dao.IAccountDao.findAccountByUid",
                        fetchType = FetchType.LAZY))
})
List<User> findAll();
  • @Many:表示一对多的关系;
  • select:指定全限定类名+方法名;
  • fetchType = FetchType.LAZY:延迟加载。

然后我们再来看这个方法:

@Select("select * from user  where id=#{id} ")
@ResultMap("userMap")
User findById(Integer userId);

他返回的是 userMap 集合,这是我们之前定义的结果集,延迟加载的好处就在于你不用的话他就不会查出来,可以看到这里就没用到 accounts,所以这里不会查出来,只会查出来 User,就是我们想要的。

注解开启二级缓存

在对应的 dao 接口上面添加 cacheNameSpace 注解开启缓存。

七、问题解决

中文乱码问题:

  • 首先要确保数据库使用的编码为 UTF-8
  • 然后在连接数据库时:jdbc.url=jdbc:mysql:///eesy?useUnicode=true&characterEncoding=UTF-8

巨人的肩膀:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Tomcat 的使用及原理分析(IDEA版)

    之前我们的角色是用户,将来我们的角色内容提供者,让别人通过浏览器来访问我们写的项目。

    wsuo
  • 虚拟机中安装双系统

    这里可能有人会问,为什么安装 win10 不安装win7 啥的,because win10 免费啊!(说着说着就哭了 )

    wsuo
  • SpringCloud Sleuth 分布式请求链路追踪

    所以就有了 Spring Cloud Sleuth ,并且提供了一套完整的服务跟踪的解决方案。

    wsuo
  • MyBatis框架教程「实践与工具类封装」

    上一篇文章我们学习了MyBatis框架的环境搭建以及对sqlsessionfactory有个大致的了解,这篇文章就要运用搭建好的环境进行增删改查并且指出一些细节...

    用户1093975
  • Spring Boot(六)集成 MyBatis 操作 MySQL 8

    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC代码和手动设置参数以及获取结果集。

    Java中文社群-磊哥
  • 程序员不得不知的软技能

    程序员群体不应该是一直低头敲代码,更应该掌握一些软技能,改变一贯的沉闷木讷的形象,让自己在竞争中胜出,从而职业发展更顺利。在此总结整理了几个常用软技能点供大家参...

    架构精进之路
  • SSM框架(六)Mybatis实现一对多和多对一查询

    HcodeBlogger
  • 有关SQL的面试题

    对于第一题,我联想到leetcode的第N高的薪水的问题,没错第一题最好采用函数的方法,将 N = 10 来作为参数输入

    润森
  • 摊牌了!我要手写一个“Spring Boot”

    我们经常会对自己早期写的代码感觉恶心,这是导致很多项目烂尾的很重要的一个原因之一。

    Guide哥
  • 看完这篇文章,99%的人都会使用Mysql Explain工具

    注意:本文基于mysql5.7进行操作,各个版本的mysql使用Explan会有微小的差异

    黎明大大

扫码关注云+社区

领取腾讯云代金券