zoukankan      html  css  js  c++  java
  • MyBatis

    概述

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

    数据库准备

    CREATE DATABASE 
    IF NOT EXISTS `study` 
    CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_bin';
    USE `study`;
    -- ----------------------------
    -- Table structure for user
    -- ----------------------------
    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
      `username` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '用户名',
      `password` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '用户密码',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='用户表';
    
    -- ----------------------------
    -- Records of user
    -- ----------------------------
    BEGIN;
    INSERT INTO `user` VALUES (1, 'admin', 'admin');
    INSERT INTO `user` VALUES (2, 'user', '123456');
    COMMIT;
    

    使用步骤

    编写核心配置文件

    <?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://localhost:3306/study?characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=Asia/Shanghai"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
        <!--所有Mapper.xml都需要在这里注册才能使用-->
        <mappers>
            <mapper resource="org/example/dao/UserMapper.xml"/>
        </mappers>
    </configuration>
    

    编写工具类

    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
    
        static {
            try {
                InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * SqlSession包含了操作数据库的所有方法
         */
        public static SqlSession getSqlSession() {
            return sqlSessionFactory.openSession();
        }
    }
    

    创建实体类

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
        private Integer id;
        private String username;
        private String password;
    }
    

    创建Mapper接口

    public interface UserMapper {
        List<User> getUserList();
    }
    

    编写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="org.example.dao.UserMapper">
        <!--id为接口方法名。返回类型单个或多个都写类类型-->
        <select id="getUserList" resultType="org.example.entity.User">
            select * from user;
        </select>
    </mapper>
    

    运行测试

    @Test
    public void testGetList() {
        // 1.获取SqlSession
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            // 2.获取UserMapper接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 3.执行方法
            List<User> userList = userMapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

    可能会遇到的问题

    • 找不到Mapper.xml:Maven资源导出失败,修改pom.xml

    基本CRUD

    增删改需要提交事务

    带参查询

    增加接口方法

    /**
     * 根据id获取用户
     */
    User getUserById(int id);
    

    编写SQL

    <select id="getUserById" resultType="org.example.entity.User" parameterType="int">
        select * from user where id = #{id};
    </select>
    

    测试

    @Test
    public void testGetUser() {
        // 1.获取SqlSession
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            // 2.获取UserMapper接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 3.执行方法
            User user = userMapper.getUserById(1);
            System.out.println(user);
        }
    }
    

    插入单条

    增加接口方法

    /**
     * 插入用户
     */
    int addUser(User user);
    

    编写SQL

    <!-- #里面要跟实体类属性名相同 -->
    <insert id="addUser" parameterType="org.example.entity.User" >
        insert into `user`(`username`, `password`) values(#{username}, #{password});
    </insert>
    

    测试

    @Test
    public void testAddUser() {
        // 1.获取SqlSession
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            // 2.获取UserMapper接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 3.执行方法
            int i = userMapper.addUser(new User(null, "root", "root"));
            System.out.println(i); // 1,没必要写,插入失败直接报错
            // 4.手动提交事务
            sqlSession.commit();
        }
    }
    

    修改单条

    增加接口方法

    /**
     * 修改用户
     */
    int updateUser(User user);
    

    编写SQL

    <!--会自动返回受影响的行数-->
    <update id="updateUser" parameterType="org.example.entity.User">
        update `user` set `username` = #{username}, `password` = #{password} where `id` = #{id};
    </update>
    

    测试

    @Test
    public void testUpdateUser() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 会自动返回受影响的行数,看情况判断
            userMapper.updateUser(new User(5, "root", "rootroot"));
    
            sqlSession.commit();
        }
    }
    

    删除单条

    增加接口方法

    /**
     * 删除用户
     */
    int deleteUser(int id);
    

    编写SQL

    <delete id="deleteUser" parameterType="int">
        delete from `user` where id = #{id};
    </delete>
    

    测试

    @Test
    public void testDeleteUser() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            userMapper.deleteUser(5);
    
            sqlSession.commit();
        }
    }
    

    模糊查询

    增加接口方法

    /**
     * 模糊查询用户
     */
    List<User> getUserByNameLike(String nameLike);
    

    编写SQL

    <select id="getUserByNameLike" resultType="org.example.entity.User">
        select * from study.user where username like #{nameLike}
        <!-- select * from foo where bar like concat('%', #{value}, '%') -->
    </select>
    

    测试

    @Test
    public void testGetUserLike() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            List<User> userByNameLike = userMapper.getUserByNameLike("%m%");
            for (User user : userByNameLike) {
                System.out.println(user);
            }
        }
    }
    

    多参数查询Map

    万能Map,如果参数过多,应该考虑使用Map

    增加接口方法

    /**
     * 查询用户 Map版
     */
    User getUserByMap(Map<String, Object> map);
    

    编写SQL

    <select id="getUserByMap" parameterType="map" resultType="org.example.entity.User">
        <!-- #号里与map的key对应 -->
        select * from `user` where `username` = #{user} and `password` = #{pwd}
    </select>
    

    测试

    @Test
    public void testGetUser2() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            Map<String, Object> map = new HashMap<>();
            map.put("user", "admin");
            map.put("pwd", "admin");
    
            User user = userMapper.getUserByMap(map);
            System.out.println(user);
    
            sqlSession.commit();
        }
    }
    

    配置解析

    environments

    配置多套运行环境,配置第三方数据源

    properties

    可以通过配置文件动态设置属性

    编写db.properties

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/study?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username=root
    password=root
    

    引入db.properties

    注意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>
        <!--引入外部配置文件-->
        <properties resource="db.properties"/>
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>
        <!--所有Mapper.xml都需要在这里注册才能使用-->
        <mappers>
            <mapper resource="org/example/dao/UserMapper.xml"/>
        </mappers>
    </configuration>
    

    typeAliases

    给Java类型起一个短的名字

    内建别名

    别名 映射的类型
    _byte byte
    _long long
    _short short
    _int int
    _integer int
    _double double
    _float float
    _boolean boolean
    string String
    byte Byte
    long Long
    short Short
    int Integer
    integer Integer
    double Double
    float Float
    boolean Boolean
    date Date
    decimal BigDecimal
    bigdecimal BigDecimal
    object Object
    map Map
    hashmap HashMap
    list List
    arraylist ArrayList
    collection Collection
    iterator Iterator

    手动指定别名

    <typeAliases>
        <typeAlias alias="User" type="org.example.entity.User"/>
    </typeAliases>
    

    扫描包添加别名

    默认为类名首字母小写

    <typeAliases>
        <package name="org.example.entity"/>
    </typeAliases>
    

    也可以添加注解手动指定别名

    @Alias("user")
    public class User {
        ...
    }
    

    settings

    日志工厂

    STDOUT_LOGGING:

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    

    LOG4J(需要导包):

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    

    log4j.properties

    # 将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下 面的代码
    log4j.rootLogger=DEBUG,console,file
    
    # 控制台输出的相关设置
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
    
    # 文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/mybatis.log
    log4j.appender.file.MaxFileSize=10mb
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
    
    #日志输出级别
    log4j.logger.org.mybatis=DEBUG
    log4j.logger.java.sql=DEBUG
    log4j.logger.java.sql.Statement=DEBUG
    log4j.logger.java.sql.ResultSet=DEBUG
    log4j.logger.java.sql.PreparedStatement=DEBUG
    

    程序中的使用:

    @Test
    public void shouldAnswerWithTrue()
    {
        Logger logger = Logger.getLogger(AppTest.class);
        logger.info("info:进入selectUser方法");
        logger.debug("debug:进入selectUser方法");
        logger.error("error: 进入selectUser方法");
    }
    

    下划线转驼峰

    <settings>
        <!--开启数据库下划线转实体类属性驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    

    mappers

    方式一:

    <!-- 使用相对路径的资源引用 -->
    <mappers>
        <mapper resource="org/example/dao/UserMapper.xml"/>
    </mappers>
    

    方式二:
    必须在同一包下,配置文件名称和接口名称一致

    <!-- 使用映射接口实现类的完全限定类名 -->
    <mappers>
        <mapper class="org.example.dao.UserMapper"/>
    </mappers>
    

    方式三:
    可以注解xml同时存在

    <!-- 将包内的映射器接口实现全部注册为映射器 -->
    <mappers>
        <package name="org.example.dao"/>
    </mappers>
    

    resultMap

    解决列名和实体属性名不一致导致的问题

    数据库表名区分大小写,列名不区分大小写

    mybatis会根据这些查询的列名(会将列名转化为小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到返回null ; 【自动映射】

    <!-- 结果集映射 -->
    <resultMap id="userMap" type="user">
        <!--<result column="id" property="id"/> 一样的不用写-->
        <result column="password" property="pwd"/>
        <result column="username" property="user"/>
    </resultMap>
    
    <select id="getUserList" resultMap="userMap">
        select * from study.user;
    </select>
    

    多对一

    搭建环境

    student:

    -- ----------------------------
    -- Table structure for student
    -- ----------------------------
    DROP TABLE IF EXISTS `student`;
    CREATE TABLE `student` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '学生id',
      `name` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '学生姓名',
      `teacher_id` int(11) NOT NULL COMMENT '老师id',
      PRIMARY KEY (`id`),
      KEY `fk_stu_teach` (`teacher_id`),
      CONSTRAINT `fk_stu_teach` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
    
    -- ----------------------------
    -- Records of student
    -- ----------------------------
    BEGIN;
    INSERT INTO `student` VALUES (1, '张三', 1);
    INSERT INTO `student` VALUES (2, '李四', 1);
    INSERT INTO `student` VALUES (3, '王五', 1);
    INSERT INTO `student` VALUES (4, 'John', 2);
    INSERT INTO `student` VALUES (5, 'Tom', 2);
    INSERT INTO `student` VALUES (6, 'Tim', 2);
    COMMIT;
    

    teacher:

    -- ----------------------------
    -- Table structure for teacher
    -- ----------------------------
    DROP TABLE IF EXISTS `teacher`;
    CREATE TABLE `teacher` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '老师id',
      `name` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '老师姓名',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
    
    -- ----------------------------
    -- Records of teacher
    -- ----------------------------
    BEGIN;
    INSERT INTO `teacher` VALUES (1, '张老师');
    INSERT INTO `teacher` VALUES (2, '李老师');
    COMMIT;
    

    建立表对应实体类、Mapper接口和Mapper.xml

    student class

    teacher class

    项目结构

    PS:这样可以注解xml同时存在

    Mapper映射

    增加接口方法

    /**
     * 查询所有学生,及学生对应的老师
     */
    List<Student> getStudentList();
    

    编写SQL

    <!-- 子查询方式,按照查询嵌套 -->
    <resultMap id="studentMap" type="student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="teacherId" column="teacher_id"/>
        <!-- association对象 -->
        <association property="teacher" column="teacher_id" javaType="teacher" select="getTeacherById"/>
    </resultMap>
    <select id="getStudentList" resultMap="studentMap">
        select * from study.student
    </select>
    <select id="getTeacherById" resultType="teacher" parameterType="int">
        select * from study.teacher where `id` = #{id}
    </select>
    

    测试

    @Test
    public void testGetStudentList() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    
            List<Student> studentList = mapper.getStudentList();
            for (Student student : studentList) {
                System.out.println(student);
            }
        }
    }
    

    按照结果嵌套

    <!-- 一条sql连接查询 -->
    <resultMap id="studentMap" type="student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="teacherId" column="teacher_id"/>
        <association property="teacher" javaType="teacher">
            <result property="id" column="teacher_id"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>
    
    <select id="getStudentList" resultMap="studentMap">
        SELECT s.id sid, s.`name` sname, s.teacher_id, t.id tid, t.`name` tname
        FROM study.student s
        JOIN study.teacher t ON s.teacher_id = t.id
    </select>
    

    一对多

    新增接口方法

    /**
     * 查询所有老师,及老师对应的学生集合
     */
    List<Teacher> getTeacherList();
    

    编写SQL

    <resultMap id="teacherMap" type="teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="studentList" ofType="student">
            <result property="id" column="sid"/>
            <result property="name" column="sid"/>
            <result property="teacherId" column="teacher_id"/>
        </collection>
    </resultMap>
    <select id="getTeacherList" resultMap="teacherMap">
        SELECT t.id tid, t.`name` tname, s.id sid, s.`name` sname, s.teacher_id
        FROM study.student s
        JOIN study.teacher t ON s.teacher_id = t.id
    </select>
    

    测试

    @Test
    public void testGetTeacherList() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    
            List<Teacher> studentList = mapper.getTeacherList();
            System.out.println(studentList.size());
            for (Teacher teacher : studentList) {
                System.out.println(teacher);
            }
        }
    }
    

    按照查询嵌套

    <resultMap id="teacherMap" type="teacher">
        <!--当有多个column映射列时,只能映射一个,多个要手动写出来-->
        <result property="id" column="id"/>
        <collection property="studentList" column="id" javaType="arraylist" ofType="student" select="getStudentsByTeacherId"/>
    </resultMap>
    <select id="getTeacherList" resultMap="teacherMap">
        select * from study.teacher
    </select>
    <select id="getStudentsByTeacherId" resultType="student" parameterType="int">
        select * from study.student where teacher_id = #{id}
    </select>
    

    分页查询

    limit分页

    增加接口方法

    /**
     * 分页查询
     */
    List<User> getUserListByLimit(Map<String, Object> map);
    

    编写SQL

    <select id="getUserListByLimit" parameterType="map" resultType="user">
        select * from study.user limit #{startIndex},#{pageSize}
    </select>
    

    测试

    @Test
    public void testGetUserListByLimit() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            Map<String, Object> map = new HashMap<>();
            map.put("startIndex", 0);
            map.put("pageSize", 2);
    
            List<User> userList = userMapper.getUserListByLimit(map);
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

    RowBounds分页

    增加接口方法

    /**
     * 分页查询
     */
    List<User> getUserListByLimit(Map<String, Object> map);
    

    编写SQL

    <select id="getUserListByRowBounds"  resultType="user">
        select * from study.user
    </select>
    

    测试

    @Test
    public void testGetUserListByRowBounds() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            int currentPage = 2;
            int pageSize = 2;
    
            RowBounds rowBounds = new RowBounds((currentPage - 1) * pageSize, pageSize);
    
            // 通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了]
            List<User> userList = sqlSession.selectList("org.example.dao.UserMapper.getUserListByRowBounds", null, rowBounds);
    
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

    分页插件

    添加Maven依赖

    <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</version>
    </dependency>
    

    在mybatis-config.xml中定义

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- 使用MySQL方言的分页 -->
            <property name="helperDialect" value="mysql"/>
            <!-- pageSize=0查询全部结果 -->
            <property name="pageSizeZero" value="true"/>
        </plugin>
    </plugins>
    

    在需要分页的查询前使用

    @Test
    public void testGetUserListByPageHelper() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            PageHelper.startPage(1, 2);
    
            List<User> userList = userMapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
    
            // 分页信息
            PageInfo<User> pageInfo = new PageInfo<>(userList);
            System.out.println("当前页码:" + pageInfo.getPageNum());
            System.out.println("每页的记录数:" + pageInfo.getPageSize());
            System.out.println("总记录数:" + pageInfo.getTotal());
            System.out.println("总页码:" + pageInfo.getPages());
            System.out.println("是否第一页:" + pageInfo.isIsFirstPage());
            System.out.println("是否最后一页:" + pageInfo.isIsLastPage());
        }
    }
    

    注解使用

    在mybatis的核心配置文件中注入,不需要mapper.xml了:

    <mappers>
        <mapper class="org.example.dao.UserMapper"/>
    </mappers>
    

    设置自动提交事务:

    /**
     * 自动提交SqlSession
     */
    public static SqlSession getSqlSession(boolean autoCommit) {
        return sqlSessionFactory.openSession(autoCommit);
    }
    

    查询

    接口方法上添加注解

    /**
     * 获取全部用户
     */
    @Select("select * from user")
    List<User> getUserList();
    

    测试

    @Test
    public void testGetList() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            List<User> userList = userMapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

    查询单条

    接口方法上添加注解

    /**
     * 根据id获取用户
     * Param注解建议基本类型和String都加上,#可以防止sql注入
     */
    @Select("select * from user where id = #{xxid}")
    User getUserById(@Param("xxid") int id);
    

    测试

    @Test
    public void testGetUser() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            User user = userMapper.getUserById(1);
            System.out.println(user);
        }
    }
    

    新增一条

    接口方法上添加注解

    /**
     * 插入用户
     * Param注解引用类型不用加
     */
    @Insert("insert into user(username, password) values(#{username}, #{password})")
    int addUser(User user);
    

    测试

    @Test
    public void testAddUser() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            userMapper.addUser(new User(null, "root123", "root123"));
        }
    }
    

    更新一条

    接口方法上添加注解

    /**
     * 修改用户
     */
    @Update("update `user` set `username` = #{username}, `password` = #{password} where id = #{id}")
    int updateUser(User user);
    

    测试

    @Test
    public void testUpdateUser() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            userMapper.updateUser(new User(20, "root", "rootroot"));
        }
    }
    

    删除一条

    接口方法上添加注解

    /**
     * 删除用户
     */
    @Delete("delete from `user` where id = #{id}")
    int deleteUser(int id);
    

    测试

    @Test
    public void testDeleteUser() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            userMapper.deleteUser(20);
        }
    }
    

    万能Map

    接口方法上添加注解

    /**
     * 查询用户 Map版
     */
    @Select("select * from `user` where `username` = #{user} and `password` = #{pwd}")
    User getUserByMap(Map<String, Object> map);
    

    测试

    @Test
    public void testGetUserByMap() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            Map<String, Object> map = new HashMap<>();
            map.put("user", "admin");
            map.put("pwd", "admin");
    
            User user = userMapper.getUserByMap(map);
            System.out.println(user);
        }
    }
    

    动态SQL

    if

    添加接口方法

    /**
     * 条件查询
     */
    List<User> getUserByCondition(Map<String, Object> map);
    

    编写SQL

    <select id="getUserByCondition" parameterType="map" resultType="user">
        select * from study.user where 1=1
        <if test="id != null">
            and id = #{id}
        </if>
        <if test="username != null">
            and username = #{username}
        </if>
        <if test="password != null">
            and password = #{password}
        </if>
    </select>
    

    测试

    @Test
    public void testGetUserByCondition() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            Map<String, Object> map = new HashMap<>();
    //        map.put("username", "admin");
    
            List<User> userList = userMapper.getUserByCondition(map);
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

    choose

    <select id="getUserByCondition" parameterType="map" resultType="user">
        select * from study.user
        <where>
            <choose>
                <when test="username != null">
                    and username = #{username}
                </when>
                <when test="password != null">
                    and password = #{password}
                </when>
                <otherwise>
                    and id = #{id}
                </otherwise>
            </choose>
        </where>
    </select>
    
    @Test
    public void testGetUserByCondition() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            Map<String, Object> map = new HashMap<>();
            map.put("id", 1);
            map.put("username", "admin");
    
            List<User> userList = userMapper.getUserByCondition(map);
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

    trim

    where

    where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除

    <select id="getUserByCondition" parameterType="map" resultType="user">
        select * from study.user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="username != null">
                and username = #{username}
            </if>
            <if test="password != null">
                and password = #{password}
            </if>
        </where>
    </select>
    

    set

    set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号

    接口新增方法:

    /**
     * 条件更新
     */
    int updateUserByCondition(Map<String, Object> map);
    

    编写SQL:

    <update id="updateUserByCondition" parameterType="map">
        update study.user
        <set>
            <if test="username != null">username = #{username}, </if>
            <if test="password != null">password = #{password}, </if>
        </set>
        where id = #{id}
    </update>
    

    测试:

    @Test
    public void testUpdateUserByCondition() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            Map<String, Object> map = new HashMap<>();
            map.put("id", 3);
            map.put("username", "root");
            map.put("password", "root");
    
            userMapper.updateUserByCondition(map);
        }
    }
    

    foreach

    当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值

    新增接口方法

    /**
     * in查询
     */
    List<User> getUserByIds(List<Integer> list);
    

    编写SQL

    <select id="getUserByIds" parameterType="list" resultType="user">
        select * from study.user
        <where>
            <foreach collection="list" item="id" open="id in (" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>
    

    测试

    @Test
    public void testGetUserByIds() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            List<Integer> list = new ArrayList<>();
            list.add(1);
            list.add(2);
            list.add(3);
    
            List<User> userList = userMapper.getUserByIds(list);
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

    SQL片段

    <update id="updateUserByCondition" parameterType="map">
        update study.user
        <set>
            <include refid="if-username-password"></include>
        </set>
        where id = #{id}
    </update>
    
    <!-- 最好只提取if -->
    <sql id="if-username-password">
        <if test="username != null">username = #{username}, </if>
        <if test="password != null">password = #{password}, </if>
    </sql>
    

    缓存

    简介

    1. 什么是缓存 [ Cache ]?
      存在内存中的临时数据。
      将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
    2. 为什么使用缓存?
      减少和数据库的交互次数,减少系统开销,提高系统效率。
    3. 什么样的数据能使用缓存?
      经常查询并且不经常改变的数据。

    Mybatis缓存

    • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
    • MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
      • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
      • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
      • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

    一级缓存

    一级缓存也叫本地缓存

    • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库

    失效的情况

    • sqlSession不同,每个sqlSession中的缓存相互独立
    • sqlSession相同,查询条件不同,当前缓存中,不存在这个数据
    • sqlSession相同,两次查询之间执行了增删改操作,可能会对当前数据产生影响
    • sqlSession相同,手动清除一级缓存,sqlSession.clearCache();

    一级缓存就是一个map

    二级缓存

    二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

    基于namespace级别的缓存,一个名称空间,对应一个二级缓存

    工作机制:不同的mapper查出的数据会放在自己对应的缓存(map)中

    使用步骤

    1. 开启全局缓存 【mybatis-config.xml】
    <setting name="cacheEnabled" value="true"/>
    
    1. 去每个mapper.xml中配置使用二级缓存【xxxMapper.xml】
    <!--置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,
    而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者 产生冲突。-->
    <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
    

    测试

    所有的实体类先实现序列化接口,readOnle默认是false,需要序列化,改为true即只读,不需要序列化。序列化是深拷贝,所以反序列化后的对像和原对象不是同一个对象,故哈希值不同

    结论

    • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
    • 查出的数据都会被默认先放在一级缓存中
    • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

    自定义EhCache

    Ehcache是一种广泛使用的java分布式缓存,用于通用缓存

    Maven引入

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

    编写ehcache.xml文件

    如果在 加载时 未找到 /ehcache.xml 资源或出现问题,则将使用默认配置

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache>
        <!--
        diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。
        参数解释如下: 
        user.home – 用户主目录 
        user.dir – 用户当前工作目录 
        java.io.tmpdir – 默认临时文件路径
        -->
        <diskStore path="java.io.tmpdir"/>
    
        <defaultCache
                maxEntriesLocalHeap="1000"
                eternal="false"
                timeToIdleSeconds="1200"
                timeToLiveSeconds="3000"
                diskSpoolBufferSizeMB="30"
                maxEntriesLocalDisk="10000"
                diskExpiryThreadIntervalSeconds="1200"
                memoryStoreEvictionPolicy="LRU"
                statistics="false">
            <persistence strategy="localTempSwap"/>
        </defaultCache>
    
        <cache name="harryBootCache"
               eternal="false"
               timeToIdleSeconds="1200"
               timeToLiveSeconds="3000"
               maxElementsInMemory="1000"
               memoryStoreEvictionPolicy="FIFO"/>
    
        <!--
        name:缓存名称
        maxElementsInMemory:缓存最大个数
        eternal:对象是否永久有效,一但设置了,timeout将不起作用
        timeToIdleSeconds:设置对象在失效前允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0,也就是对象存活时间无穷大。
        overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
        maxElementsOnDisk:硬盘最大缓存个数
        diskPersistent:是否缓存虚拟机重启期数据
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最少使用)。你可以设置为FIFO(先进先出)或是LFU
        clearOnFlush:内存数量最大时是否清除
          -->
    </ehcache>
    

    在mapper.xml中使用

    <mapper namespace = “org.acme.FooMapper” >
        <cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
    </mapper>
    
  • 相关阅读:
    Distinct Values
    树状数组求逆序数
    Color the ball
    Cube
    树状数组
    手动编写JQUERY插件
    JQuery和原生JS跨域加载JSON数据或HTML。
    使用CEF(CEFGLUE)作为您的客户端UI(一)
    给IIS添加网站配置权限
    SQL SERVER 报:由于数据移动,未能继续以 NOLOCK 方式扫描错误的解决办法。
  • 原文地址:https://www.cnblogs.com/shenleg/p/14276595.html
Copyright © 2011-2022 走看看