• 周六. 7 月 27th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

07、JavaEE–Mybatis

admin

11 月 28, 2021

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);
    }
}

发表回复