「查缺补漏」— Mybatis其实很简单

什么是Mybatis?

Mybatis是一个持久层框架(ORM),简单理解就是对JDBC的封装。

ORMObject Relationship Mapping对象关系映射。

  • 对象:面向对象
  • 关系:关系型数据库
  • 映射:Java到Mysql的映射,开发者可以用面向对象的思想来管理数据库。

使用步骤

  1. 引入依赖
  2. Mybatis的配置文件
  3. 编写配置文件
  4. 注册
  5. 关闭

为什么用Mybatis?

优点

  • 与JDBC相比,减少了50%以上的代码量
  • 简单易学
  • 灵活;SQL是写在XML中的,降低耦合度,便于统一管理和优化
  • 提供XML标签,支持编写动态SQL语句
  • 提供映射标签,支持对象与数据库的ORM字段关系映射。

缺点

  • SQL语句需要亲自编写,工作量大。(hibernate能够自动生成SQL语句)
  • SQL语句依赖于数据库,可移植性差,不能随意更换数据库。

怎么用Mybatis?

Mybatis核心接口

  1. 新建Maven工程,在pom.xml中引入相关依赖。

    <!--mybatis依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    <!--mysql数据库驱动jar-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.25</version>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>
    
  2. 配置mybatis-config.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>
        <!--配置Mybatis的运行环境-->
        <environments default="development">
            <environment id="development">
                <!--配置JDBC事物-->
                <transactionManager type="JDBC"/>
                <!--POOLED配置JDBC数据源链接池-->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=UTC"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>
    
  3. 新建数据库表

    CREATE TABLE `user` (
      `id` int NOT NULL AUTO_INCREMENT,
      `username` varchar(50) DEFAULT NULL,
      `password` varchar(255) DEFAULT NULL,
      `age` int DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    
  4. 新建数据库表对应的实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private Integer id;
        private String username;
        private String password;
        private Integer age;
    }
    
  5. 开发DAO接口

    ​ – mybatis中,接口中不能定义方法的重载。

    public interface UserDao {
        //保存用户
        int save(User user);
    }
    
  6. 开发Mapper配置文件

    ​ – 在mybatis中,一个DAO接口对应一个Mapper配置文件

    ​ – Resources下创建文件夹要用/分隔。(com/ibear/mapper)

    <?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文件是对哪个DAO接口的实现
                   全限定名:包.类
    -->
    <mapper namespace="com.ibear.dao.UserDao">
        <!--保存
            insert:插入操作
            id:接口中对应的方法名
            parameterType:和接口中对应方法的参数类型一致 -> 全限定名:包.类
            (#{id},#{username},#{password},#{age}): 需要与数据库列名一致,顺序也要一致
            useGeneratedKeys="true":使用数据库的id自动生成策略(加不加都行,此策略只支持mysql)
            keyProperty="id":主键属性:当使用数据库主键自动生成策略时,将自动返回生成的id
        -->
        <insert id="save" parameterType="com.ibear.entity.User" keyProperty="id" useGeneratedKeys="true">
            insert into `user` values (#{id},#{username},#{password},#{age})
        </insert>
    </mapper>
    
  7. mapper注册到mybatis-config配置文件中

    <!--注册项目中mapper.xml配置-->
    <mappers>
        <mapper resource="com/ibear/mapper/UserDao.xml"/>
    </mappers>
    
  8. 测试

    public class TestMybatis {
        public static void main(String[] args) throws IOException {
            //读取mybatis-config.xml
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //创建mybatis核心对象SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            //获取sqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //获取DAO对象
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            try {
                User user = new User();
                user.setUsername("leehom");
                user.setPassword("123");
                user.setAge(22);
                int count = userDao.save(user);
                System.out.println("影响的条数:" + count);
                //数据库当前保存这条记录的id
                System.out.println("本次所添加记录的id:" + user.getId());
                sqlSession.commit(); //提交事务
            } catch (Exception e) {
                e.printStackTrace();
                sqlSession.rollback(); //提交事务
            } finally {
                sqlSession.close(); //释放资源
            }
        }
    }
    

Mybatis的CRUD

  1. UserDao.java

    public interface UserDao {
        //保存用户
        int save(User user);
    
        //更新用户
        int update(User user);
    
        //删除操作
        int delete(Integer id);
    
        //查询所有
        List<User> queryAll();
    
        //查询一个(基于ID)
        User queryByID(Integer id);
    
        //模糊查询like(`%国%`)
        List<User> queryLikeByUsername(String username);
    
        //分页查询
        //startIndex:起始位置的索引 pageSize:每页显示记录数
        List<User> queryByPage(@Param("start") Integer startIndex,@Param("rows") Integer pageSize);
    
        //查询总条数
        Long queryTotalCounts();
    }
    
  2. UserDao.xml(mapper文件)

    <?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文件是对哪个DAO接口的实现
                   全限定名:包.类
    -->
    <mapper namespace="com.ibear.dao.UserDao">
        <!--保存
            insert:插入操作
            id:接口中对应的方法名
            parameterType:和接口中对应方法的参数类型一致 -> 全限定名:包.类
            (#{id},#{username},#{password},#{age}): 需要与数据库列名一致,顺序也要一致
            useGeneratedKeys="true":使用数据库的id自动生成策略(加不加都行,此策略只支持mysql)
            keyProperty="id":主键属性:当使用数据库主键自动生成策略时,将自动返回生成的id
        -->
        <insert id="save" parameterType="com.ibear.entity.User" keyProperty="id" useGeneratedKeys="true">
            insert into `user` values (#{id}, #{username}, #{password}, #{age})
        </insert>
    
        <!--修改-->
        <update id="update" parameterType="com.ibear.entity.User">
            update `user`
            <set>
                <if test="username!=null and username!=''">
                    username=#{username},
                </if>
                <if test="password!=null and password!=''">
                    password=#{password},
                </if>
                <if test="age!=null and age!=''">
                    age=#{age},
                </if>
            </set>
            where id=#{id}
        </update>
    
        <!--删除-->
        <delete id="delete" parameterType="Integer">
            delete from `user` where id = #{id}
        </delete>
    
        <!--查询所有-->
        <!--
        sql标签:用户sql片段的复用
            id:相当于给sql标签中内容定义一个唯一的标识
            include:引入sql标签
            userQuery:被引入标签的名称
        -->
        <sql id="userQuery">
            id, username, password, age
        </sql>
        <select id="queryAll" resultType="com.ibear.entity.User">
            select
            <include refid="userQuery"/>
            from `user`
        </select>
    
        <!--查询一个(基于ID)-->
        <select id="queryByID" parameterType="Integer" resultType="com.ibear.entity.User">
            select
            <include refid="userQuery"/>
            from `user`
            where id=#{id}
        </select>
    
        <!--模糊查询like(`%国%`)-->
        <select id="queryLikeByUsername" parameterType="String" resultType="com.ibear.entity.User">
            select
            <include refid="userQuery"/>
            from `user`
            where username like concat('%',#{username},'%')
        </select>
        
        <!--分页查询-->
        <!--多个参数时不用写parameterType-->
        <select id="queryByPage" resultType="com.ibear.entity.User">
            select
            <include refid="userQuery"/>
            from `user`
            limit #{startIndex},#{pageSize}
        </select>
    
        <!--查询总条数-->
        <select id="queryTotalCounts" resultType="Long">
            select count(id) from `user`
        </select>
    </mapper>
    
  3. mybatis-config.xml(注册配置)

    <!--注册项目中mapper.xml配置-->
    <mappers>
        <mapper resource="com/ibear/mapper/UserDao.xml"/>
    </mappers>
    
  4. 封装MybatisUtils工具类

    public class MybatisUtil {
        private static SqlSessionFactory sqlSessionFactory;
    
        //静态代码块 :类加载的时候执行;只执行一次
        static {
            //读取mybatis-config.xml配置文件
            InputStream is = null;
            try {
                is = Resources.getResourceAsStream("mybatis-config.xml");
                //创建mybatis核心对象SqlSessionFactory
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        //提供sqlSession
        public static SqlSession getSqlSession() {
            //获取sqlSession,执行sql语句
            return sqlSessionFactory.openSession();
        }
    
        //关闭sqlSession
        public static void close(SqlSession sqlSession) {
            sqlSession.close();
        }
    }
    
  5. test测试

    public class TestCRUD {
        //保存操作
        @Test
        public void save() throws IOException {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            try {
                //获取DAO对象
                UserDao userDao = sqlSession.getMapper(UserDao.class);
                User user = new User();
                user.setUsername("成龙");
                user.setPassword("123");
                user.setAge(22);
                int count = userDao.save(user);
                System.out.println("影响的条数:" + count);
                //数据库当前保存这条记录的id
                System.out.println("本次所添加记录的id:" + user.getId());
                sqlSession.commit(); //提交事务
            } catch (Exception e) {
                e.printStackTrace();
                sqlSession.rollback(); //提交事务
            } finally {
                MybatisUtil.close(sqlSession); //释放资源
            }
        }
    
        //更新操作
        //如果需要实现"只修改想修改的,不将未修改的值赋为null";需要使用动态sql。
        @Test
        public void update() throws IOException {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            try {
                //获取DAO对象
                UserDao userDao = sqlSession.getMapper(UserDao.class);
                User user = new User();
                user.setId(26);
                user.setUsername("擎天柱");
                user.setAge(1234);
                userDao.update(user);
                sqlSession.commit();
            } catch (Exception e) {
                e.printStackTrace();
                sqlSession.rollback();
            } finally {
                MybatisUtil.close(sqlSession);
            }
        }
    
        //删除操作
        @Test
        public void delete() throws IOException {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            try {
                //获取DAO对象
                UserDao userDao = sqlSession.getMapper(UserDao.class);
                userDao.delete(27);
                sqlSession.commit();
            } catch (Exception e) {
                e.printStackTrace();
                sqlSession.rollback();
            } finally {
                MybatisUtil.close(sqlSession);
            }
        }
    
        //查询所有(查询不需要操作事务)
        @Test
        public void queryAll() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            List<User> users = userDao.queryAll();
            users.forEach(user -> System.out.println(user));
            MybatisUtil.close(sqlSession);
        }
    
        //查询一个(基于ID)
        @Test
        public void queryByID() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            User user = userDao.queryByID(1);
            System.out.println(user);
            MybatisUtil.close(sqlSession);
        }
    
        //模糊查询like(`%国%`)
        @Test
        public void queryLikeByUsername() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            List<User> users = userDao.queryLikeByUsername("国");
            users.forEach(user -> System.out.println(user));
            MybatisUtil.close(sqlSession);
        }
    
        //分页查询
        @Test
        public void queryByPage() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            List<User> users = userDao.queryByPage(0, 3);
            users.forEach(user -> System.out.println(user));
            MybatisUtil.close(sqlSession);
        }
    
        //查询总条数
        @Test
        public void queryTotalCounts() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            Long counts = userDao.queryTotalCounts();
            System.out.println("总记录数:" + counts);
            MybatisUtil.close(sqlSession);
        }
    }
    

注意事项

  1. parameterType为包装类时,可以避免传入参数为“null”导致的报错。

  2. 传入多个参数时,parameterType不用写。

    <!--根据用户名和年龄查询-->
    <select id="findByNameAndAge" resultType="com.ibear.entity.Account">
        select * from `user`
        where username = #{param 1} and age = #{param 2}
    </select>
    
  3. resultType&resultMap的区别

    • 两者都是对数据库中的返回结果进行封装
    • resultType 只能封装简单类型的对象
      • 简单类型对象:对象中没有对象类型的属性
    • resultMap 可以封装复杂类型的对象
      • 多用于处理库表关联关系(一对一、一对多、多对多)
  4. #{}${}的区别

    • #{}是预编译处理

      • Mybatis在处理#{}时,会将sql中的#{}替换成?号,调用PreparedStatement的set方法来赋值。
    • ${}是字符串替换

      • Mybatis在处理 时 , 就 是 把 ‘ {}时,就是把` {}替换成变量的值`。

    使用#{}可以有效防止SQL注入,提高系统的安全性。

多表联查

  • 一对一
  • 一对多
  • 多对多

一对一

外键可以放在任意一方

  1. 建表t_person & t_info

    create table t_person(
    	id int(6) primary key auto_increment,
    	name varchar(40),
    	age int(3),
    	cardno varchar(18) references t_info(cardno)
    );
    
    create table t_info(
    	id int(6) primary key auto_increment,
    	cardno varchar(18),
    	address varchar(100)
    );
    
  2. 实现实体类entity

    Info

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Info {
        private Integer id;
        private String cardno;//身份证号
        private String address;//地址
    }
    

    Person

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Person {
        private Integer id;
        private String name;
        private Integer age;
        private String cardno;  //外键信息
        private Info info;  //关系属性
    }
    
  3. 添加方法

    InfoDao

    public interface InfoDao {
        //保存
        void save(Info info);
    }
    

    PersonDao

    public interface PersonDao {
        //保存
        void save(Person person);
        //查询所有
        List<Person> queryAll();
    }
    
  4. mapper

    <mapper namespace="com.ibear.dao.InfoDao">
        <!--保存-->
        <insert id="save" parameterType="com.ibear.entity.Info" keyProperty="id" useGeneratedKeys="true">
            insert into `t_info` values (#{id}, #{cardno}, #{address})
        </insert>
    </mapper>
    
    <?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.ibear.dao.PersonDao">
        <!--保存-->
        <insert id="save" parameterType="com.ibear.entity.Person" keyProperty="id" useGeneratedKeys="true">
            insert into `t_person`
            values (#{id}, #{name}, #{age}, #{cardno})
        </insert>
    
        <!--查询所有-->
        <!--用于处理结果封装-->
        <resultMap id="personMap" type="com.ibear.entity.Person">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <result column="age" property="age"/>
            <result column="cardno" property="cardno"/>
    
            <!--处理一对一
                association:用于处理一对一关联关系标签
                property:用于书写封装的关系属性名
                javaType:关系属性的java类型
            -->
            <association property="info" javaType="com.ibear.entity.Info">
                <id column="iid" property="id"/>
                <result column="icardno" property="cardno"/>
                <result column="address" property="address"/>
            </association>
        </resultMap>
        <select id="queryAll" resultMap="personMap">
            select p.id,p.name,p.age,p.cardno,
                   i.id iid,i.cardno icardno,i.address
            from `t_person` p
            left join `t_info` i
            on p.cardno = i.cardno
        </select>
    </mapper>
    
  5. 注册mapper配置

    <!--注册项目中mapper.xml配置-->
    <mappers>
        <!--身份信息-->
        <mapper resource="com/ibear/mapper/InfoDao.xml"/>
        <!--用户信息-->
        <mapper resource="com/ibear/mapper/PersonDao.xml"/>
    </mappers>
    
  6. test测试

    TestInfoDao

    public class TestInfoDao {
        @Test
        public void save() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            try {
                InfoDao infoDao = sqlSession.getMapper(InfoDao.class);
                Info info = new Info();
                info.setCardno("123456789123456789");
                info.setAddress("北京朝阳区");
                infoDao.save(info);
                sqlSession.commit();
            } catch (Exception e) {
                e.printStackTrace();
                sqlSession.rollback();
            } finally {
                MybatisUtil.close(sqlSession);
            }
        }
    }
    

    TestPersonDao

    public class TestPersonDao {
        //保存用户信息
        @Test
        public void save() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            try {
                PersonDao personDao = sqlSession.getMapper(PersonDao.class);
                Person person = new Person();
                person.setName("张三");
                person.setAge(18);
                person.setCardno("123456789123456789");
                personDao.save(person);
                sqlSession.commit();
            } catch (Exception e) {
                e.printStackTrace();
                sqlSession.rollback();
            } finally {
                MybatisUtil.close(sqlSession);
            }
        }
    
        //查询所有用户信息
        @Test
        public void queryAll() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            PersonDao personDao = sqlSession.getMapper(PersonDao.class);
            personDao.queryAll().forEach(person -> {
                System.out.println("当前用户信息:" + person);
            });
            MybatisUtil.close(sqlSession);
        }
    }
    

一对多

外键最好放在多的一方

  1. 建表

    t_dept & t_emp

    -- 部门表
    create table t_dept(
    	id int(6) primary key auto_increment,
    	name varchar(40)
    );
    
    -- 员工表
    create table t_emp(
    	id int(6) primary key auto_increment,
    	name varchar(40),
    	age int(3),
    	bir timestamp,
    	deptid int(6) references t_dept(id)
    );
    
  2. 创建实体类

    Dept

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Dept {
        private Integer id;
        private String name;
        private List<Emp> emps;//关系属性
    }
    

    Emp

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Emp {
        private Integer id;
        private String name;
        private Integer age;
        private Date bir;
    }
    
  3. Dao

    public interface DeptDao {
        //查询所有部门并将每个部门的员工信息查询出来
        List<Dept> queryAll();
    }
    
  4. Mapper

    <?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.ibear.dao.DeptDao">
        <resultMap id="deptMap" type="com.ibear.entity.Dept">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <!--
                collection:用于处理"一对多"时的标签
                property:用于封装关系属性名
                javaType:关系属性类型
                ofType:用于书写关系属性类型中泛型的类型
            -->
            <collection property="emps" javaType="list" ofType="com.ibear.entity.Emp">
                <id column="eid" property="id"/>
                <result column="ename" property="name"/>
                <result column="age" property="age"/>
                <result column="bir" property="bir"/>
            </collection>
        </resultMap>
        <select id="queryAll" resultMap="deptMap">
            select d.id,d.name,
                   e.id eid,e.name ename,e.age,e.bir
            from t_dept d
            left join t_emp e
            on d.id = e.deptid
        </select>
    </mapper>
    
  5. 注册

    <mappers>
        <mapper resource="com/ibear/mapper/DeptDao.xml"/>
    </mappers>
    
  6. 测试

    public class TestDeptDao {
        //查询所有部门信息
        @Test
        public void queryAll() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            DeptDao deptDao = sqlSession.getMapper(DeptDao.class);
            deptDao.queryAll().forEach(dept -> {
                System.out.println("部门信息:" + dept);
                dept.getEmps().forEach(emp -> {
                    System.out.println("    员工信息:" + emp);
                    System.out.println("==============");
                });
            });
            MybatisUtil.close(sqlSession);
        }
    }
    

多对一

  1. 实体类Emp添加关系属性

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Emp {
        private Integer id;
        private String name;
        private Integer age;
        private Date bir;
        private Dept dept;//关系属性
    }
    
  2. EmpDao

    public interface EmpDao {
        //查询所有员工并将每个员工的部门信息查询出来
        List<Emp> queryAll();
    }
    
  3. Mapper

    <?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.ibear.dao.EmpDao">
        <resultMap id="empMap" type="com.ibear.entity.Emp">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <result column="age" property="age"/>
            <result column="bir" property="bir"/>
            <!--
                association:用于处理"一对一"时的标签
                property:用于封装关系属性名
                javaType:关系属性类型
                ofType:用于书写关系属性类型中泛型的类型
            -->
            <association property="dept" javaType="com.ibear.entity.Dept">
                <id column="did" property="id"/>
                <result column="dname" property="name"/>
            </association>
        </resultMap>
        <select id="queryAll" resultMap="empMap">
            select e.id, e.name, e.age, e.bir,
                   d.id did, d.name dname
            from t_emp e
            left join t_dept d
            on e.deptid = d.id
        </select>
    </mapper>
    
  4. 注册

    <mappers>
        <mapper resource="com/ibear/mapper/EmpDao.xml"/>
    </mappers>
    
  5. 测试

    public class TestEmpDao {
        //查询所有部门信息
        @Test
        public void queryAll() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            EmpDao empDao = sqlSession.getMapper(EmpDao.class);
            empDao.queryAll().forEach(emp -> {
                System.out.println("员工信息:" + emp + "部门信息:" + emp.getDept());
            });
            MybatisUtil.close(sqlSession);
        }
    }
    

多对多

  1. 建表

    -- 学生表	
    create table t_student(
    	id int(6) primary key auto_increment,
    	name varchar(6)
    );
    
    -- 课程表
    create table t_course(
    	id int(6) primary key auto_increment,
    	name varchar(6)
    );
    
    -- 中间表
    create table t_student_course(
    	id int(6) primary key auto_increment,
    	sid int(6) references t_student(id),
    	cid int(6) references t_course(id)
    );
    
  2. 创建实体类

    Student

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Student {
        private Integer id;
        private String name;
        private List<Course> course;//关系属性
    }
    

    Course

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Course {
        private Integer id;
        private String name;
        private List<Student> student;//关系属性
    }
    
  3. Dao

    public interface StudentDao {
        //根据id查询
        Student queryByID(Integer id);
    }
    
  4. Mapper

    <?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.ibear.dao.StudentDao">
        <resultMap id="studentMap" type="com.ibear.entity.Student">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <!--关系属性-->
            <collection property="course" javaType="list" ofType="com.ibear.entity.Course">
                <id column="cid" property="id"/>
                <result column="cname" property="name"/>
            </collection>
        </resultMap>
        <select id="queryByID" parameterType="Integer" resultMap="studentMap">
            select s.id, s.name,
                   c.id cid, c.name cname
            from t_student s
                     left join t_student_course sc
                               on s.id = sc.sid
                     left join t_course c
                               on sc.cid = c.id
            where s.id = #{id}
        </select>
    </mapper>
    
  5. 注册

    <mappers>
        <!--课程信息-->
        <mapper resource="com/ibear/mapper/CourseDao.xml"/>
    </mappers>
    
  6. 测试

    public class TestStudentDao {
        //根据id查询学生信息
        @Test
        public void queryByID() {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
            Student student = studentDao.queryByID(1);
            System.out.println("学生信息:" + student);
            student.getCourse().forEach(course -> {
                System.out.println("课程信息:" + course.getName());
            });
            MybatisUtil.close(sqlSession);
        }
    }
    

逆向工程 - MBG

MBG:MyBatis Generator(MyBatis的逆向工程)

MyBatis框架需要:实体类Mapper接口Mapper.xml这三个组件。

传统开发需要开发者手动创建上面三个组件,但是逆向工程可以帮助自动创建这三个组件,减轻开发者的工作量。

使用方法

  1. 新建Maven工程,pom.xml

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.7</version>
        </dependency>
    </dependencies>
    
  2. 创建 MBG 配置文件:generatorConfig.xml

MyBatis延迟加载

什么是延迟加载?

延迟加载也称为懒加载,使用延迟加载可以提高程序的运行效率。

MyBatis缓存

什么是MyBatis缓存?

使用缓存可以减少java应用与数据库的交互次数,从而提升程序的运行效率。

缓存分类
一级缓存
  • SqlSession级别,默认开启,不能关闭。

操作数据库时需要创建SqlSession对象,在对象中有一个HashMap用于存储缓存数据,不同的SqlSession之间缓存数据区域互不影响。

一级缓存的作用域是SqlSession范围的,当同一个SqlSession中执行两次相同的SQL语句时,第一次执行完后会将数据保存到缓存中,第二次查询时会直接从缓存中获取。

需要注意的是:如果SqlSession执行了DML操作(insert,update,delete),MyBatis必须将缓存清空以保证数据的准确性。

二级缓存
  • Mapper级别,默认关闭,可以开启。

MyBatis动态SQL

if

​ – 只有当<if>标签内的条件满足时,才会将<if>标签的内容拼接到sql语句中。

where

​ – <where>后面不可以接sql语句

<select id="findUser" resultType="com.ibear.entity.User">
   select * from `user` 
   <where>
     <if test="id!=null and id!=''">
	    id = #{id}
	 </if>
     <if test="name!=null and name!=''">
	    and name = #{name}
	 </if>
   </where>
</select>

set

​ –<set><where>类似,后面可以拼接sql语句。

<update id="updateUser">
   update `user`
   <set>
	   <if test="name!=null and name!=''">
		  name = #{name},
	   </if>
	   <if test="age!=null and age!=''">
		  age = #{age}
	   </if>
   </set>
   where id = #{id}
</update>

choose

​ – 类似java中的switch;不管满不满足条件,先执行一次。

<select id="findUser" resultType="com.ibear.entity.User">
   select * from `user`
   <choose>
      <when test="id!=null and id!='' and name!=null and name!=''">
	      id = #{id} and name = #{name}
	  </when>
	  <when test="id!=null and id!=''">
	      id = #{id}
	  </when>
	  <otherwise>
	      name = #{name}
	  </otherwise>
   </choose>
</select>

foreach

​ – 可以遍历List、Map、Array

  • collection:指定需要遍历的元素
  • item:遍历之后的每一项
  • separator:定义foreach里面语句的分隔符
  • index:map中代表key,数组中代表索引。
<select id="findAll" resultType="com.ibear.entity.User">
    select * from `user` where id in
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
       #{id}
    </foreach>
</select>

<insert id="batchInsertUser">
    insert into `user`(id,name,age)
    values
    <foreach collection="users" item="user" separator=",">
       (#{user.id},#{user.name},#{user.age})
    </foreach>
</insert>

<update id="updateUser">
    update `user`
	<set>
		<foreach collection="map" item="val" index="key" separator=",">
       		${key}=#{val}
    	</foreach>
	</set>
	where id = #{id}
</update>

bind

​ – 在标签内创建一个变量,提前预处理好变量,再放进语句内拼接;常用于模糊查询。

<select id="findUserById" resultType="com.ibear.entity.User">
    <bind name="userName" value="name+'%'"/>
    select * from `user` where name = #{userName}
</select>

参考文献

MyBatis官方文档

十分钟搞懂Lombok使用与原理

MyBatis 框架使用

这份Mybatis总结,我觉得你很需要!

🖥【编程不良人】2021Mybatis基础入门篇

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值