• 07、JavaEE--Mybatis


    MyBatis

    MyBatis是一个优秀的持久化框架,它对JDBC的操作数据库的过程进行封装,使开发者只关注SQL本身,而不需要花费精力去处理注册驱动、创建connection、创建statement、手动设置参数和结果集检索等JDBC繁杂的过程代码。

    MyBatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由MyBatis框架执行sql,并将结果映射成java对象进行返回。

    首先我们来创建一个mybatis_db的数据库:

    set foreign_key_checks=0;
    -- ----------------------------
    -- table structure for `orders`
    -- ----------------------------
    drop table if exists `orders`;
    create table `orders` (
      `id` int(11) not null auto_increment,
      `user_id` int(11) not null comment '下单用户id',
      `number` varchar(32) not null comment '订单号',
      `createtime` datetime not null comment '创建订单时间',
      `note` varchar(100) default null comment '备注',
      primary key (`id`),
      key `fk_orders_1` (`user_id`),
      constraint `fk_orders_id` foreign key (`user_id`) references `user` (`id`) on delete no action on update no action
    ) engine=innodb auto_increment=6 default charset=utf8;
    -- ----------------------------
    -- records of orders
    -- ----------------------------
    insert into `orders` values ('3', '1', '1000010', '2015-02-04 13:22:35', null);
    insert into `orders` values ('4', '1', '1000011', '2015-02-03 13:22:41', null);
    insert into `orders` values ('5', '10', '1000012', '2015-02-12 16:13:23', null);
    -- ----------------------------
    -- table structure for `user`
    -- ----------------------------
    drop table if exists `user`;
    create table `user` (
      `id` int(11) not null auto_increment,
      `username` varchar(32) not null comment '用户名称',
      `birthday` date default null comment '生日',
      `sex` char(1) default null comment '性别',
      `address` varchar(256) default null comment '地址',
      primary key (`id`)
    ) engine=innodb auto_increment=27 default charset=utf8;
    -- ----------------------------
    -- records of user
    -- ----------------------------
    insert into `user` values ('1', '王五', null, '2', null);
    insert into `user` values ('10', '张三', '2014-07-10', '1', '北京市');
    insert into `user` values ('16', '张小明', null, '1', '河南郑州');
    insert into `user` values ('22', '陈小明', null, '1', '河南郑州');
    insert into `user` values ('24', '张三丰', null, '1', '河南郑州');
    insert into `user` values ('25', '陈小明', null, '1', '河南郑州');
    insert into `user` values ('26', '王五', null, null, null);
    

    JDBC

    使用JDBC操作数据库使用如下代码进行:

    public class JdbcDemo {
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                // 加载数据库驱动
                Class.forName("com.mysql.jdbc.Driver");
                // 通过驱动管理类获取数据库链接
                connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis_db?characterEncoding=utf-8", "root", "123456");
                // 定义sql语句 ?表示占位符
                String sql = "select * from user where username = ?";
                // 获取预处理statement
                preparedStatement = connection.prepareStatement(sql);
                // 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
                preparedStatement.setString(1, "王五");
                // 向数据库发出sql执行查询,查询出结果集
                resultSet = preparedStatement.executeQuery();
                // 遍历查询结果集
                while (resultSet.next()) {
                    System.out.println(resultSet.getString("id") + "  " + resultSet.getString("username"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 释放资源
                if (resultSet != null) {
                    try {
                        resultSet.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (preparedStatement != null) {
                    try {
                        preparedStatement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    从上面的操作可以看出JDBC所存在的问题:

    1、频繁创建数据库连接和释放导致资源浪费,从而影响性能,可以使用数据库连接池解决。
    2、sql语句在代码中是硬编码,造成不易维护,经常性需要修改代码。
    3、占位符的位置是硬编码,一旦改变位置就容易导致错误。
    4、对结果集解析存在硬编码,sql变化则导致解析代码变化,系统不易维护。

    MyBatis

    MyBatis架构

    Mybatis架构图如下所示:

    • Mybatis介绍

    Mybatis主要包含配置文件和映射文件,分别如下所示:

    SqlMapConfig.xml:此文件作为mybatis的全局配置文件,配置了Mybatis的运行环境等信息。

    mapper.xml文件是sql映射文件,文件中配置操作数据库的sql语句,它需要在sqlMapConfig.xml中加载。

    通过Mybatis环境等配置信息构造SqlSessionFactory会话工程。由会话工厂创建SqlSession会话,操作数据库由SqlSession进行。Mybatis底层自定义Executor执行器接口操作数据库,该接口有两个实现,基本执行器和缓存执行器。Mapped Statement也是Mybatis底层封装对象,它包装Mybatis配置信息及sql映射信息等。

    • Mybatis配置

      SqlMapConfig.xml中配置的内容和顺序如下:

    标签 描述
    properties 属性,配置在properties中的属性可以通过表达式的方式引用。
    settings 全局配置参数
    typeAliases 类型别名
    typeHandlers 类型处理器
    objectFactory 对象工厂
    plugins 插件
    evironments 环境集合属性对象,它包含如下子集: environment:环境子属性对象 transactionManager:事务管理 dataSource:数据源
    mappers 映射器
    • properties

      SqlMapConfig.xml可以引用java属性文件中的配置信息:

    在resources下定义db.properties文件,如下所示:

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis_db?characterEncoding=utf-8
    jdbc.username=root
    jdbc.password=123456
    

    SqlMapConfig.xml引用如下:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <properties resource="db.properties"/>
        <!-- 和spring整合后 environments配置将废除-->
        <environments default="development">
            <environment id="development">
                <!-- 使用jdbc事务管理 -->
                <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>
        <!--Mapper的位置-->
        <mappers>
            <!--resource目录下的全路径,这样则可以不用编写dao层实现类-->
            <mapper resource="sqlmap/User.xml"></mapper>
        </mappers>
    </configuration>
    

    注意: MyBatis 将按照下面的顺序来加载属性:

    在 properties元素体内定义的属性首先被读取。
    然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。

    • 自定义类型别名

      首先在SqlMapConfig.xml中配置自定义类型别名

    <typeAliases>
        <!-- 单个别名定义 -->
        <typeAlias alias="user" type="com.legend.mybatis.pojo.User" />
        <!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
        <package name="com.legend.mybatis.pojo" />
        <package name="其它包" />
    </typeAliases>
    

    在mapper.xml配置文件中,就可以使用设置的别名,别名大小写不敏感。也可以写成USER。

    <?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.legend.mybatis.mapper.UserMapper">
        <insert id="insertUser" parameterType="user">
            insert into user (username, birthday, address, sex) values (#{username}, #{birthday}, #{address}, #{sex})
        </insert>
    </mapper>
    

    当然我们还可以引入某一个包下的所有类:

    <typeAliases>
        <!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
        <package name="com.legend.mybatis.pojo"/>
    </typeAliases>
    
    • mappers

      Mapper配置的几种方法:

    1、使用相对于类路径的资源(现在的使用方式)

    <mapper resource="sqlmap/User.xml" />
    

    2、使用mapper接口类路径,此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

    <mapper class="com.legend.mybatis.mapper.UserMapper"/>
    

    3、注册指定包下的所有mapper接口,此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

    <package name="com.legend.mybatis.mapper"/>
    

    MyBatis使用

    使用MyBatis之前需要引入它的核心包,可以使用maven直接构建:

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.41</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>
    
    • 开发步骤

    1、根据创建的数据库表来简历pojo映射表名的序列化类:

    public class User implements Serializable {
        private Integer id;
        private String username;// 用户姓名
        private String sex;// 性别
        private Date birthday;// 生日
        private String address;// 地址
        ...省略Get和Set方法...
        @Override
        public String toString() {
            return "User [id=" + id + ", username=" + username + ", sex=" + sex
                    + ", birthday=" + birthday + ", address=" + address + "]";
        }
    }
    

    2、在resources目录下创建sqlMapConfig.xml配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <properties resource="db.properties"/>
        <!-- 和spring整合后 environments配置将废除-->
        <environments default="development">
            <environment id="development">
                <!-- 使用jdbc事务管理 -->
                <transactionManager type="JDBC" />
                <!-- 数据库连接池 -->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver" />
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis_db?characterEncoding=utf-8" />
                    <property name="username" value="root" />
                    <property name="password" value="123456" />
                </dataSource>
            </environment>
        </environments>
        <!--Mapper的位置-->
        <mappers>
            <mapper resource="sqlmap/User.xml"/>
        </mappers>
    </configuration>
    

    如果在没有使用Spring的情况下则需要配置enviroments标签中的内容来配置连接信息,如果有Spring的话则不需要配置。

    3、在resources目录下创建log4j.properties用来在控制台打印日志信息

    # Global logging configuration
    log4j.rootLogger=DEBUG, stdout
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    

    4、在resources下创建sqlmap文件夹,如果要映射User表,则在sqlmap下创建User.xml文件,用来编写sql语句

    <?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">
    <!-- 写Sql语句   -->
    <mapper namespace="test">
        <!--通过id查询用户
            id:唯一标识符,和namespace配合使用
            parameterType:占位符的类型
            resultType:返回值的类型,必须指定全包名
        -->
        <select id="findUserById" parameterType="Integer" resultType="com.legend.mybatis.pojo.User">
            select * from user where id = #{value}
        </select>
    </mapper>
    

    5、编写测试类进行测试

    public class Demo {
        // 通过id查询用户
        @Test
        public void findByID() throws Exception {
            // 1、加载核心配置文件
            InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
            // 2、创建SqlSessionFactory
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
            // 3、创建SqlSession
            SqlSession sqlSession = sessionFactory.openSession();
            // 4、执行Sql语句
            User user = sqlSession.selectOne("test.findUserById", 10);
            System.out.println(user);
        }
    }
    

    增删改查(CRUD)

    • 添加用户

      Mybatis不但可以直接添加用户,还可以在添加用户后立刻返回该用户的id值,其中<selectKey>标签就表示返回值的配置。

    <?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="test">
        <!--添加用户-->
        <insert id="insertUser" parameterType="com.legend.mybatis.pojo.User">
            <selectKey keyProperty="id" resultType="Integer" order="AFTER">
                <!--保存用户后直接返回最新id值-->
                select LAST_INSERT_ID()
            </selectKey>
            insert into user (username, birthday, address, sex) values (#{username}, #{birthday}, #{address}, #{sex})
        </insert>
    </mapper>
    

    测试代码如下:

    @Test
    public void insertUser() throws Exception {
        // 1、加载核心配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、执行Sql语句
        User user = new User();
        user.setUsername("陈冠希");
        user.setBirthday(new Date());
        user.setAddress("香港");
        user.setSex("男");
        sqlSession.insert("test.insertUser", user);
        sqlSession.commit();
        System.out.println(user.getId());
    }
    
    • 删除用户

      通过id来删除用户比较简单,下面的删除的操作

    <?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="test">
        <!--删除用户-->
        <delete id="deleteUserById" parameterType="Integer">
            delete from user where id = #{value}
        </delete>
    </mapper>
    

    测试代码如下:

    @Test
    public void deleteUser() throws Exception {
        // 1、加载核心配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、执行Sql语句
        sqlSession.delete("test.deleteUserById", 30);
        sqlSession.commit();
    }
    
    • 更新用户

      通过id进行更新用户的时候,在测试代码中传入给User.xml中的是User对象,因为修改用户信息的操作是在User.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="test">
        <!--更新用户-->
        <update id="updateUserById" parameterType="com.legend.mybatis.pojo.User">
            update user set username=#{username}, sex = #{sex}, birthday=#{birthday}, address=#{address}
            where id = #{id}
        </update>
    </mapper>
    

    测试代码如下:

    @Test
    public void updateUser() throws Exception {
        // 1、加载核心配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、执行Sql语句
        User user = new User();
        user.setId(30);
        user.setUsername("周杰伦");
        user.setBirthday(new Date());
        user.setAddress("台湾");
        user.setSex("男");
        int update = sqlSession.update("test.updateUserById", user);
        sqlSession.commit();
    }
    
    • 查找用户

      由于查找用户在上方已经演示过,这里演示通过用户名模糊查询用户列表

    <?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="test">
        <select id="findByUsername" parameterType="String" resultType="com.legend.mybatis.pojo.User">
            select * from user where username like '%${value}%'
        </select>
    </mapper>
    

    注意:#{}的形式表示的是占位符;({}表示的是字符串的拼接,并且必须是){value}的格式,否则会抛出异常信息。

    测试代码如下:

    // 通过用户名模糊查询用户列表
    @Test
    public void findByUserName() throws Exception{
        // 1、加载核心配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、执行Sql语句
        List<User> list = sqlSession.selectList("test.findByUsername", "五");
        for (User user : list) {
            System.out.println(user);
        }
    }
    

    Mapper动态代理

    Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法一样。

    Mapper接口开发需要遵循如下规范:

    1、Mapper.xml文件中的namespace与mapper接口的类路径相同。
    2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
    3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
    4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

    Mapper使用

    1、创建名称为UserMapper的接口,并定义增删改查的方法。

    public interface UserMapper {
        // 添加用户
        void insertUser(User user);
        // 删除用户
        void deleteUserById(Integer id);
        // 修改用户
        void updateUserById(User user);
        // 查找用户
        User findUserById(Integer id);
    }
    

    2、创建Mapper.xml配置文件,在resources/sqlmap下创建名称为User.xml,名称空间必须是UserMapper的全包名路径

    <?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.legend.mybatis.mapper.UserMapper">
        <!--添加用户-->
        <insert id="insertUser" parameterType="com.legend.mybatis.pojo.User">
            insert into user (username, birthday, address, sex) values (#{username}, #{birthday}, #{address}, #{sex})
        </insert>
        <!--删除用户-->
        <delete id="deleteUserById" parameterType="Integer">
            delete from user where id = #{value}
        </delete>
        <!--更新用户-->
        <update id="updateUserById" parameterType="com.legend.mybatis.pojo.User">
            update user set username=#{username}, sex = #{sex}, birthday=#{birthday}, address=#{address}
            where id = #{id}
        </update>
        <!--查找用户-->
        <select id="findUserById" parameterType="Integer" resultType="com.legend.mybatis.pojo.User">
            select * from user where id = #{value}
        </select>
    </mapper>
    

    3、编写测试类MapperTest进行测试

    public class MapperTest {
        // 新增用户
        @Test
        public void insertUser() throws IOException {
            // 1、加载核心配置文件
            InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
            // 2、创建SqlSessionFactory
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
            // 3、创建SqlSession
            SqlSession sqlSession = sessionFactory.openSession();
            // 4、动态代理创建Mapper类
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = new User();
            user.setUsername("legend");
            user.setAddress("中国");
            user.setSex("男");
            user.setBirthday(new Date());
            mapper.insertUser(user);
            sqlSession.commit();
            sqlSession.close();
        }
        // 删除用户
        @Test
        public void deleteUserById() throws IOException {
            // 1、加载核心配置文件
            InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
            // 2、创建SqlSessionFactory
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
            // 3、创建SqlSession
            SqlSession sqlSession = sessionFactory.openSession();
            // 4、动态代理创建Mapper类
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            mapper.deleteUserById(32);
            sqlSession.commit();
            sqlSession.close();
        }
        // 修改用户
        @Test
        public void updateUserById() throws IOException {
            // 1、加载核心配置文件
            InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
            // 2、创建SqlSessionFactory
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
            // 3、创建SqlSession
            SqlSession sqlSession = sessionFactory.openSession();
            // 4、动态代理创建Mapper类
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = new User();
            user.setId(26);
            user.setUsername("legend");
            user.setAddress("中国");
            user.setSex("男");
            user.setBirthday(new Date());
            mapper.updateUserById(user);
            sqlSession.commit();
            sqlSession.close();
        }
        // 查找用户
        @Test
        public void findUserById() throws IOException {
            // 1、加载核心配置文件
            InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
            // 2、创建SqlSessionFactory
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
            // 3、创建SqlSession
            SqlSession sqlSession = sessionFactory.openSession();
            // 4、动态代理创建Mapper类
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.findUserById(26);
            System.out.println(user);
            sqlSession.close();
        }
    }
    

    Mybatis标签

    输入参数

    • pojo包装类

    1、首先在SqlMapConfig.xml中配置引入pojo包下的所有类,方便后续使用

    <typeAliases>
        <package name="com.legend.mybatis.pojo"/>
    </typeAliases>
    

    2、创建包装类对象,其中包含User对象

    /**
     * 包装类对象为什么要序列化:
     * 因为对象在创建时是存在于内存中,当对象传输的时候会先转换为二进制进行传输,
     * 当对方拿到二进制数据后又转换为对象存在于内存中使用,中间有序列化和反序列化的过程。
     */
    public class QueryVo implements Serializable {
        private User user;
        public User getUser() {
            return user;
        }
        public void setUser(User user) {
            this.user = user;
        }
    }
    

    3、创建UserMapper.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.legend.mybatis.mapper.UserMapper">
        <!--根据用户名模糊查询-->
        <select id="findUserByQueryVo" parameterType="QueryVo" resultType="User">
            select * from user where username like "%"#{user.username}"%"
        </select>
    </mapper>
    

    4、编写Mapper接口,并创建Usermapper.xml中的方法

    public interface UserMapper {
        // 根据用户名模糊查询
        List<User> findUserByQueryVo(QueryVo queryVo);
    }
    

    5、编写测试代码进行测试

    // 根据用户名模糊查询
    @Test
    public void findUserByQueryVo() throws Exception {
        // 1、加载核心配置文件
        String resource = "sqlMapConfig.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、动态代理创建Mapper类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        QueryVo vo = new QueryVo();
        User user = new User();
        user.setUsername("五");
        vo.setUser(user);
        List<User> users = mapper.findUserByQueryVo(vo);
        for(User u : users) {
            System.out.println(u);
        }
    }
    

    输出参数

    输出参数这里只演示简单参数和ResultMap对象,关于输出pojo对象和pojo列表参数上方增删改查的例子。

    • 简单类型

    1、创建UserMapper.xml,并编写查询用户总数的语句

    <!--查询用户总记录数-->
    <select id="queryCount" resultType="Integer">
        select count(*) from user;
    </select>
    

    2、对应的Mapper接口中的方法

    public interface UserMapper {
        // 查询用户总数
        Integer queryCount();
    }
    

    3、编写测试类进行测试

    // 查询用户总数
    @Test
    public void queryUserCount() throws Exception {
        // 1、加载核心配置文件
        String resource = "sqlMapConfig.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、动态代理创建Mapper类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Integer count = mapper.queryCount();
        System.out.println(count);
    }
    
    • ResultMap

    ResultType可以指定将查询结果映射为pojo,但是需要pojo属性名和被查询表的字段名一致才能映射成功。如果被查询表的字段名和pojo属性不一致的话,可以通过resultMap将字段名和属性名建立对应关系。

    范例:查询订单表order的所有数据。

    1、创建pojo类Orders,其中只有userId和数据库字段名不一致

    public class Orders  implements Serializable{
        private Integer id;
        private Integer user_id;
        private String number;
        private Date createtime;
        private String note;
        ...省略Get和Set方法...
        @Override
        public String toString() {
            return "Orders [id=" + id + ", user_id=" + user_id + ", number=" + number + ", createtime=" + createtime
                    + ", note=" + note + "]";
        }
    }
    

    2、创建OrderMapper.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.legend.mybatis.mapper.OrderMapper">
        <select id="selectOrdersList" resultType="com.legend.mybatis.pojo.Orders">
            select id, user_id, number, createtime, note from orders
        </select>
    </mapper>
    

    3、在SqlMapConfig中引入OrderMapper.xml文件

    <!--Mapper的位置-->
    <mappers>
        <mapper resource="sqlmap/OrderMapper.xml"></mapper>
    </mappers>
    

    4、编写OrderMapper接口,并实现OrderMapper.xml中定义的方法

    public interface OrderMapper {
        // 查询订单表Order的所有数据
        List<Orders> selectOrdersList();
    }
    

    5、编写测试代码

    // 查询订单表orders的所有数据
    @Test
    public void selectOrdersList() throws Exception {
        // 1、加载核心配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、动态代理创建Mapper类
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<Orders> orders = mapper.selectOrdersList();
        for (Orders order : orders) {
            System.out.println(order);
        }
    }
    

    输出结果:

    Orders [id=3, userId=null, number=1000010, createtime=Wed Feb 04 13:22:35 CST 2015, note=null]
    Orders [id=4, userId=null, number=1000011, createtime=Tue Feb 03 13:22:41 CST 2015, note=null]
    Orders [id=5, userId=null, number=1000012, createtime=Thu Feb 12 16:13:23 CST 2015, note=null]

    由于上边的mapper.xml中sql查询列(user_id)和Order类属性(userId)不一致,所以查询结果不能映射到pojo中。需要定义resultMap,把orderResultMap

    和sql查询列(user_id)和Order类属性(userId)对应起来:

    <?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.legend.mybatis.mapper.OrderMapper">
        <!--手动映射属性名和字段名-->
        <resultMap id="orderResultMap" type="Orders">
            <!--映射主键-->
            <id column="id" property="id"/>
            <!--映射表名-->
            <result column="user_id" property="userId"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createtime"/>
            <result column="note" property="note"/>
        </resultMap>
        <select id="selectOrdersList" resultMap="orderResultMap">
            select id, user_id, number, createtime, note from orders
        </select>
    </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.legend.mybatis.mapper.OrderMapper">
        <!--手动映射属性名和字段名-->
        <resultMap id="orderResultMap" type="Orders">
            <!--映射表名-->
            <result column="user_id" property="userId"/>
        </resultMap>
        
        <select id="selectOrdersList" resultMap="orderResultMap">
            select id, user_id, number, createtime, note from orders
        </select>
    </mapper>
    

    输出结果:

    Orders [id=3, userId=1, number=1000010, createtime=Wed Feb 04 13:22:35 CST 2015, note=null]
    Orders [id=4, userId=1, number=1000011, createtime=Tue Feb 03 13:22:41 CST 2015, note=null]
    Orders [id=5, userId=10, number=1000012, createtime=Thu Feb 12 16:13:23 CST 2015, note=null]

    动态SQL

    • if-where语句

    范例:根据性别和名字查询用户

    1、在UserMaper.xml中编写如下语句:

    <!--根据性别和名字查询用户-->
    <select id="findUserBySexAndUsername" resultType="User">
        select * from user
        where
        <if test="sex != null and sex != ''">
            sex = #{sex}
        </if>
        <if test="username != null and username != ''">
            and username = #{username}
        </if>
    </select>
    

    2、UserMapper接口中的方法

    // 对应的UserMapper中的方法
    public interface UserMapper {
        // 根据性别和名字查询用户
        List<User> findUserBySexAndUsername(User user);
    }
    

    3、编写测试类进行测试

    @Test
    public void findUserBySexAndUsername() throws Exception {
        // 1、加载核心配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、动态代理创建Mapper类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        //user.setUsername("张三丰");
        user.setSex("1");
        List<User> users = mapper.findUserBySexAndUsername(user);
        for (User u : users) {
            System.out.println(u);
        }
    }
    

    输出结果:

    User [id=10, username=张三, sex=1, birthday=Thu Jul 10 00:00:00 CST 2014, address=北京市]
    User [id=16, username=张小明, sex=1, birthday=null, address=河南郑州]
    User [id=22, username=陈小明, sex=1, birthday=null, address=河南郑州]
    User [id=24, username=张三丰, sex=1, birthday=null, address=河南郑州]
    User [id=25, username=陈小明, sex=1, birthday=null, address=河南郑州]

    如果加上user.setUsername("张三丰")的话,就会查询性别为1并且名称为张三丰的,则只有一条记录

    User [id=24, username=张三丰, sex=1, birthday=null, address=河南郑州]

    如果我们注释掉user.setSex("1"),然后打开user.setUsername("张三丰"),再次执行会出现SQL语法错误

    @Test
    public void findUserBySexAndUsername() throws Exception {
        // 1、加载核心配置文件
        String resource = "sqlMapConfig.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、动态代理创建Mapper类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("张三丰");
        //user.setSex("1");
        List<User> users = mapper.findUserBySexAndUsername(user);
        for (User u : users) {
            System.out.println(u);
        }
    }
    

    运行结果:

    You have an error in your SQL syntax; check the manual that corresponds to your MySQL
    select * from user where and username = ?

    所以我们可以使用where标签来解决出现的问题:

    <!--根据性别和名字查询用户-->
    <select id="findUserBySexAndUsername" resultType="User">
        select * from user
        <where>
            <if test="sex != null and sex != ''">
                sex = #{sex}
            </if>
            <if test="username != null and username != ''">
                and username = #{username}
            </if>
        </where>
    </select>
    

    注意:where标签是去掉第一个前and,不能去除后and。

    • SQL片段

      Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。

    <?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.legend.mybatis.mapper.UserMapper">
        <!--将重复的sql语句提取-->
        <sql id="selector">
            select * from user
        </sql>
        <!--根据性别和名字查询用户-->
        <select id="findUserBySexAndUsername" resultType="User">
            <include refid="selector"/>
            <where>
                <if test="sex != null and sex != ''">
                    sex = #{sex}
                </if>
                <if test="username != null and username != ''">
                    and username = #{username}
                </if>
            </where>
        </select>
    </mapper>
    
    • foreach标签

    范例:根据多个id查询用户信息

    1、根据多个id查询的话,我们在Mapper接口中定义方法就要考虑三种参数情况:

    public interface UserMapper {
        // 根据多个id查询用户信息
        public List<User> selectUsersByIds(Integer[] ints);
        public List<User> selectUsersByIds(List<Integer> ints);
        public List<User> selectUsersByIds(QueryVo vo);
    }
    

    2、这里首先来演示第三种,通过包装类传参的方式,所以我们必须在包装类中定义如下属性:

    public class QueryVo implements Serializable {
        private User user;
        private Integer[] ints;
        private List<Integer> intLists;
        ...省略Get和Set方法...
    }
    

    3、然后编写Mapper.xml中的sql语句:select * from user WHERE id in ( ? , ? , ? ) 根据该语句拼接

    <!--根据多个id查询用户信息-->
    <select id="selectUsersByIds" parameterType="QueryVo" resultType="User">
        select * from user
        <where>
            <!--
                collection:需要迭代的集合或数组名称
                item:表示迭代的项
                separator:表示分隔符
                open:表示语句的前缀部分
                close:表示语句的后缀部分
            -->
            <foreach collection="intLists" item="id" separator="," open="id in (" close=")">
                #{id}
            </foreach>
        </where>
    </select>
    

    4、编写测试类测试

    // 根据多个id查询用户
    @Test
    public void selectUsersByIds() throws Exception {
        // 1、加载核心配置文件
        InputStream in = Resources.getResourceAsStream( "sqlMapConfig.xml");
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、动态代理创建Mapper类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<Integer> lists = new ArrayList<>();
        lists.add(22);
        lists.add(26);
        lists.add(34);
        QueryVo queryVo = new QueryVo();
        queryVo.setIntLists(lists);
        List<User> users = mapper.selectUsersByIds(queryVo);
        for (User user : users) {
            System.out.println(user);
        }
    }
    

    如果我们传入的参数不是Queryvo包装类,而是array和list的话,只需要collection属性改为array或list:

    <!--根据多个id查询用户信息-->
    <select id="selectUsersByIds" parameterType="QueryVo" resultType="User">
        select * from user
        <where>
            <foreach collection="array" item="id" separator="," open="id in (" close=")">
                #{id}
            </foreach>
        </where>
    </select>
    <!--根据多个id查询用户信息-->
    <select id="selectUsersByIds" parameterType="QueryVo" resultType="User">
        select * from user
        <where>
            <foreach collection="list" item="id" separator="," open="id in (" close=")">
                #{id}
            </foreach>
        </where>
    </select>
    

    总结:

    1、当使用包装类作为参数时,collection属性填包装类的引用名称。
    2、当使用Array和List作为参数时,需要传递Array和List作为名称。

    关联查询

    因为一个订单信息只会是一个人下的订单,所以从查询订单信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的订单信息则为一对多查询,

    因为一个用户可以下多个订单。

    一对一查询

    范例:查询所有订单信息,关联查询下单用户信息。

    1、一对一的话,我们需要在Order类中创建User对象,也就是一个User对象对应多个Oders

    public class Orders  implements Serializable{
        private static final long serialVersionUID = 1L;
        private Integer id;
        private Integer userId;
        private String number;
        private Date createtime;
        private String note;
        // 附加用户对象
        private User user;
        ...省略Get和Set方法...
    }
    

    2、编写一对一的sql语句

    <resultMap type="Orders" id="result">
        <result column="id" property="id"/>
        <result column="user_id" property="userId"/>
        <result column="number" property="number"/>
        <!-- 一对一关联 -->
        <association property="user" javaType="User">
            <id column="user_id" property="id"/>
            <result column="username" property="username"/>
        </association>
    </resultMap>
    <select id="selectOrders" resultMap="result">
        SELECT
        o.id,
        o.user_id,
        o.number,
        o.createtime,
        u.username
        FROM orders o
        left join user u
        on o.user_id = u.id
    </select>
    

    测试代码如下所示:

    @Test
    public void selectOrders() throws Exception {
        // 1、加载核心配置文件
        String resource = "sqlMapConfig.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、动态代理创建Mapper类
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<Orders> orders = mapper.selectOrders();
        for (Orders order : orders) {
            System.out.println(order);
        }
    }
    

    一对多查询

    1、用户信息和订单信息为一对多关系。也就是说一个用户中包含多个订单信息

    public class User implements Serializable {
        private static final long serialVersionUID = 1L;
        private Integer id;
        private String username;// 用户姓名
        private String sex;// 性别
        private Date birthday;// 生日
        private String address;// 地址
        // 附加对象List表示多个订单
        private List<Orders> ordersList;
        ...省略Get和Set方法...
    {
    

    2、编写多对一的sql语句

    <resultMap type="User" id="result">
        <id column="user_id" property="id"/>
        <result column="username" property="username"/>
        <!-- 一对多 -->
        <collection property="ordersList" ofType="Orders">
            <id column="id" property="id"/>
            <result column="number" property="number"/>
        </collection>
    </resultMap>
    <select id="selectUserList" resultMap="result">
        select
        o.id,
        o.user_id,
        o.number,
        o.createtime,
        u.username
        from user u
        left join orders o
        on o.user_id = u.id
    </select>
    

    3、编写测试代码测试

    @Test
    public void selectUserList() throws  Exception {
        // 1、加载核心配置文件
        String resource = "sqlMapConfig.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        // 2、创建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3、创建SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        // 4、动态代理创建Mapper类
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<User> users = mapper.selectUserList();
        for (User user : users) {
            System.out.println(user);
        }
    }
    
  • 相关阅读:
    dotnet run urls=http://*:8080
    内敛声明变量
    C#反射赋值(更新时用)
    mysql---auto_increment=10啥意思呢?
    vscode--代码折叠与展开
    联想启天M410台式机的清除BIOS密码
    AntD是什么?
    react-router-dom 中文文档
    Recoil是什么?
    sql server查看建表的创建代码?
  • 原文地址:https://www.cnblogs.com/pengjingya/p/15032934.html
走看看 - 开发者的网上家园