Mybatis基础

一、原生JDBC的工具类

1.1 示例代码

  1. public class JDBCUtils{
  2. private static String driver;
  3. private static String url;
  4. private static String username;
  5. private static String password;
  6. //使用静态代码块进行初始化
  7. static{
  8. try{
  9. //读取properties配置文件中的数据
  10. InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
  11. //创建Properties对象
  12. Properties prop = new Properties();
  13. //读取流到对象中
  14. prop.load(in);
  15. //解析其中的数据
  16. driver = prop.getProperty("driver");
  17. url = prop.getProperty("url");
  18. username = prop.getProperty("username");
  19. password = prop.getProperty("password");
  20. //注册驱动
  21. Class.forName(driver);
  22. }catch(Exception e){
  23. e.printStackTrace();
  24. }
  25. }
  26. //提供链接的方法
  27. public static Connection getConn(){
  28. Connection conn = null;
  29. try{
  30. conn = DriverManager.getConnection(url,username,password);
  31. }catch(Exception e){
  32. e.printStackTrace();
  33. }
  34. return conn;
  35. }
  36. //关闭资源
  37. public static void close(Connection conn,PreparedStatement ps){
  38. if(ps != null){
  39. try{
  40. ps.close();
  41. }catch(Exception e){
  42. e.printStackTrace();
  43. }
  44. }
  45. if(conn != null){
  46. try{
  47. conn.close();
  48. }catch(Exception e){
  49. e.printStackTrace();
  50. }
  51. }
  52. }
  53. //重载形式关闭资源,查询的时候使用
  54. public static void close(Connection conn,PreparedStatement ps,ResultSet rs){
  55. if(rs != null){
  56. try{
  57. rs.close();
  58. }catch(Exception e){
  59. e.printStackTrace();
  60. }
  61. }
  62. close(conn,ps);
  63. }
  64. }

1.2 问题总结

  • 用原生的jdbc需要频繁的创建链接和关闭资源,会对数据库链接造成压力,解决这个问题用连接池
  • 用原生的jdbc编写的sql语句属于硬编码,如果对sql语句进行修改,需要修改java原代码,需要重新编译才会生效。解决方案为使用mybatis中的xml配置方式。
  • 使用原生的jdbc填充占位符也属于硬编码,对对象的映射也需要硬编码,在mybatis有映射关系映射,可以查询结束后直接得到对象。

    二、mybatis框架

  • 概念:mybatis属于轻量级的ORM框架,主要致力于解决java代码和数据库之间的交互。使用mybatis框架可以做到对象映射关系,传入参数和输出参数都可以以对象为标准。

    2.1 mybatis执行流程图

    image.png

    2.2 mybatis组件

  • SqlSessionFactoryBuilder:创建SqlSessionFactory对象。

  • SqlSessionFactory:由上一个组件得到,作用是获取会话(开启会话)
  • SqlSession:会话,属于mybatis的执行器,用来发送sql执行,并让Executor执行器进行执行sql并返回结果集。
  • Sql mapper:一般用来做mapper代理的方式进行sql语句的操作,由java接口和xml配置文件共同组成,也可以是注解的方式组成。(第二阶段开发方式)

    三、mybatis入门代码演示

    3.1 mybatis环境的搭建

    3.1.1 所需要的jar包(核心jar包和依赖jar包)

    image.png

    3.1.2 需要看api,将日志文件和全局配置文件编写好

  • 日志文件

    1. # Global logging configuration
    2. log4j.rootLogger=DEBUG, stdout
    3. # MyBatis logging configuration...
    4. log4j.logger.org.mybatis.example.BlogMapper=TRACE
    5. # Console output...
    6. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    7. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    8. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
  • 全局的配置文件

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <!DOCTYPE configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <!-- 数据源环境配置,目前是需要的,整合spring之后,不需要这一块配置 -->
    7. <environments default="development">
    8. <environment id="development">
    9. <!-- 事务的控制,选择默认的jdbc控制事务,整合spring之后,也不需要 -->
    10. <transactionManager type="JDBC"/>
    11. <!-- 数据源配置 -->
    12. <dataSource type="POOLED">
    13. <!-- 数据库驱动 -->
    14. <property name="driver" value="com.mysql.jdbc.Driver"/>
    15. <!-- 链接的路径 -->
    16. <property name="url" value="jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&amp;characterEncoding=utf-8"/>
    17. <!-- 用户名 -->
    18. <property name="username" value="root"/>
    19. <!-- 密码 -->
    20. <property name="password" value=""/>
    21. </dataSource>
    22. </environment>
    23. </environments>
    24. </configuration>

    3.2编写对应的mapper.xml配置文件

  • 该文件中时统一编写sql指令

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <!-- 先写根标签mapper
    6. namespace:目前可以理解为单个mapper的标记,分割不同的mapper标签配置,到了后面的mapper代理
    7. 方式,会有其他更加重要的特殊含义,目前随意取名
    8. -->
    9. <mapper namespace="userTest">
    10. <!-- 查询语句:该条查询语句根据id查询,返回结果只有一个
    11. id:给该条查询语句取一个名字,在java代码调用的时候使用
    12. parameterType:输入参数的数据类型全名
    13. #{id}:表示用来接收输入的参数,可以理解成ps里面的占位符
    14. resultType:输出参数数据类型。如果是映射成model的对象,则需要写全路径名
    15. -->
    16. <select id="queryUserById" parameterType="java.lang.Integer" resultType="com.six.model.User">
    17. SELECT id,user_name,birthday,gender,address FROM t_user WHERE id = #{id}
    18. </select>
    19. </mapper>
  • 当编写了mapper.xml配置文件之后,必须要在全局的SqlMapConfig.xml文件中进行加载

    1. <mappers>
    2. <mapper resource="mapper/userMapper.xml"/>
    3. </mappers>

    3.3 测试类

    ```java public class UserDao {

    public static void main(String[] args) throws Exception {

    1. queryUserById();

    }

    public static void queryUserById() throws Exception{

    1. //读取全局的配置文件
    2. String path = "SqlMapConfig.xml";
    3. InputStream config = Resources.getResourceAsStream(path);
    4. //创建工厂
    5. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
    6. //开启会话
    7. SqlSession sqlSession = factory.openSession();
    8. /*调用方法执行
    9. * 第一个参数:定位要执行哪一条sql语句,namespace的值.查询语句的id值
    10. * 第二个参数:输入的参数数据,理解成实参
    11. */
    12. User user = (User)sqlSession.selectOne("userTest.queryUserById", 1);
    13. System.out.println(user);

    } }

  1. <a name="Iqre7"></a>
  2. ## 3.4 mybatis其他的操作
  3. <a name="9ZKfq"></a>
  4. ### 3.4.1 查询所有的User数据
  5. - userMapper.xml配置文件
  6. ```xml
  7. <!-- 查询所有的user数据,会返回多条user对象
  8. resultType:输出参数映射
  9. -->
  10. <select id="queryAllUser" resultType="com.six.model.User">
  11. SELECT id,user_name,birthday,gender,address FROM t_user
  12. </select>
  • 测试代码

    1. /**
    2. * 查询所有的用户数据,结果集为多条user对象
    3. * @throws Exception
    4. */
    5. public static void queryAllUser() throws Exception{
    6. //开启会话
    7. SqlSession sqlSession = factory.openSession();
    8. /*
    9. * 调用查询结果集为多条数据的方法
    10. */
    11. List<User> list = sqlSession.selectList("userTest.queryAllUser");
    12. System.out.println(list);
    13. }

    3.4.2 实现模糊查询

  • userMapper.xml配置文件

    1. <!-- 实现模糊查询
    2. parameterType:String类型
    3. resultType:输入参数类型
    4. ${value}:在模糊查询的时候,只能使用$,并且在大括号中只能写value
    5. -->
    6. <select id="queryUserByName" parameterType="java.lang.String" resultType="com.six.model.User">
    7. SELECT id,user_name,birthday,gender,address FROM t_user WHERE user_name LIKE "%${value}%"
    8. </select>
  • 测试代码

    1. /**
    2. * 实现模糊查询
    3. */
    4. public static void queryUserByName() {
    5. //开启会话
    6. SqlSession session = factory.openSession();
    7. //调用方法查询
    8. List<User> list = session.selectList("userTest.queryUserByName", "小");
    9. System.out.println(list);
    10. }

    3.4.3 实现新增用户数据

  • userMapper.xml配置文件

    1. <!-- 实现用户新增
    2. id:给该新增语句取一个名字
    3. parameterType:需要传入一个对象,将对象中的值传入到数据库中
    4. -->
    5. <insert id="insertUser" parameterType="com.six.model.User">
    6. INSERT INTO t_user(user_name,gender,address) value(#{user_name},#{gender},#{address})
    7. </insert>
  • 测试类

    1. public static void insertUser() {
    2. User user = new User();
    3. user.setUser_name("李华");
    4. user.setGender("男");
    5. user.setAddress("重庆");
    6. //开启会话
    7. SqlSession session = factory.openSession();
    8. //调用方法执行
    9. //返回值表示,数据库中受影响的行数,如果大于0,说明成功
    10. int row = session.insert("userTest.insertUser", user);
    11. System.out.println(row);
    12. //提交一次事务
    13. session.commit();
    14. //关闭会话
    15. session.close();
    16. }

    3.4.4 实现更新操作

  • userMapper.xml配置文件

    1. <!-- 更新数据,根据id更新 -->
    2. <update id="updateUserById" parameterType="com.six.model.User">
    3. UPDATE t_user SET user_name = #{user_name},address = #{address} WHERE id = #{id}
    4. </update>
  • 测试类

    1. public static void updateUserById() {
    2. //开启会话
    3. SqlSession session = factory.openSession();
    4. User user = new User();
    5. user.setId(1);
    6. user.setUser_name("小明明");
    7. user.setAddress("上海");
    8. //执行语句
    9. int row = session.update("userTest.updateUserById", user);
    10. System.out.println(row);
    11. //提交事务
    12. session.commit();
    13. }

    3.4.5 删除操作

  • userMapper.xml配置文件

    1. <!-- 删除语句 -->
    2. <delete id="deleteUserById" parameterType="java.lang.Integer">
    3. DELETE FROM t_user WHERE id = #{id}
    4. </delete>
  • 测试类

    1. public static void deleteUserById() {
    2. //开启会话
    3. SqlSession session = factory.openSession();
    4. int row = session.delete("userTest.deleteUserById", 1);
    5. System.out.println(row);
    6. //提交事务
    7. session.commit();
    8. }

    四、使用mybatis实现dao的模式开发

  • 使用dao实现业务逻辑里面的增、删、改、查

    五、使用Mapper代理的方式实现开发

  • 组成:mapper接口(dao接口)和mapper.xml配置文件。

要求:

  • mapper标签中namespace必须是对应mapper接口的全路径
  • id:必须和接口中方法名称一致
  • parameterType:必须和接口中方法的形式参数数据类型一致
  • resultType:必须和接口中方法的返回值数据类型一致

    5.1 实现根据用户id查询单个用户数据

  • mapper接口

    1. /**
    2. *
    3. * @ClassName: UserMapper
    4. * @Description:用户的mapper接口
    5. * @author Alon
    6. * @date 2020年11月20日 上午11:29:29
    7. *
    8. */
    9. public interface UserMapper {
    10. /**
    11. *
    12. * @Title: queryUserById
    13. * @Description: 根据用户id查询单个用户数据
    14. * @param @param id 用户的id
    15. * @param @return
    16. * @param @throws Exception
    17. * @return User 用户对象
    18. * @throws
    19. */
    20. public User queryUserById(int id) throws Exception;
    21. }
  • 编写mapper.xml配置文件

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <!-- mapper中namespace在mapper代理里面具有特殊的含义
    6. namespace:必须是对应mapper接口的全路径
    7. -->
    8. <mapper namespace = "com.six.mapper.UserMapper">
    9. <!-- mapper代理中sql语句的要求
    10. id:必须和接口中方法名称一致
    11. parameterType:输入参数必须和接口中方法括号中的形式参数数据类型一致
    12. resultType:输出参数必须和接口中方法返回值数据类型一致
    13. -->
    14. <select id="queryUserById" parameterType="java.lang.Integer" resultType="com.six.model.User">
    15. SELECT id,user_name,birthday,gender,address FROM t_user WHERE id = #{id}
    16. </select>
    17. </mapper>
  • dao实现类

    1. /**
    2. *
    3. * @ClassName: UserDaoImpl
    4. * @Description:用户dao的实现类
    5. * @author Alon
    6. * @date 2020年11月20日 上午11:37:04
    7. *
    8. */
    9. public class UserDaoImpl implements UserMapper{
    10. //声明会话工厂对象
    11. private SqlSessionFactory factory;
    12. public UserDaoImpl() {
    13. try {
    14. String path = "SqlMapConfig.xml";
    15. InputStream config = Resources.getResourceAsStream(path);
    16. factory = new SqlSessionFactoryBuilder().build(config);
    17. } catch (Exception e) {
    18. e.printStackTrace();
    19. }
    20. }
    21. /*
    22. *
    23. * <p>Title: queryUserById</p>
    24. * <p>Description: 根据id查询单个用户的值</p>
    25. * @param id
    26. * @return
    27. * @throws Exception
    28. * @see com.six.mapper.UserMapper#queryUserById(int)
    29. */
    30. @Override
    31. public User queryUserById(int id) throws Exception {
    32. //开启会话
    33. SqlSession session = factory.openSession();
    34. //获取mapper对象,名字和mapper接口名一致
    35. UserMapper mapper = session.getMapper(UserMapper.class);
    36. User user = mapper.queryUserById(id);
    37. System.out.println(user);
    38. return null;
    39. }
    40. public static void main(String[] args) throws Exception {
    41. new UserDaoImpl().queryUserById(2);
    42. }
    43. }

    其他操作,方式和上面类似

    5.2 读取properties配置文件

  • 在mybatis中,可以在全局的配置文件SqlMapConfig.xml中进行读取properties配置文件,使用的标签为:

  • 需要在开始标签里面增加一个属性:resource=”文件路径”
  • 写法:可以在数据源里面通过${key值}获取到properties中的value

    1. <configuration>
    2. <!-- 加载properties配置文件中的内容,已经实现了读取properties配置文件的内容 -->
    3. <properties resource="db.properties"></properties>
    4. <!-- 数据源环境配置,目前是需要的,整合spring之后,不需要这一块配置 -->
    5. <environments default="development">
    6. <environment id="development">
    7. <!-- 事务的控制,选择默认的jdbc控制事务,整合spring之后,也不需要 -->
    8. <transactionManager type="JDBC"/>
    9. <!-- 数据源配置 -->
    10. <dataSource type="POOLED">
    11. <!-- 数据库驱动 -->
    12. <property name="driver" value="${driver}"/>
    13. <!-- 链接的路径 -->
    14. <property name="url" value="${url}"/>
    15. <!-- 用户名 -->
    16. <property name="username" value="${username}"/>
    17. <!-- 密码 -->
    18. <property name="password" value="${password}"/>
    19. </dataSource>
    20. </environment>
    21. </environments>
    22. <mappers>
    23. <mapper resource="mapper/userMapper.xml"/>
    24. </mappers>
    25. </configuration>

    5.3 别名的配置

  • 主要用来配置自定义的pojo类,每次编写输入映射和输出映射的时候,都需要写全路径名,比较麻烦也比较容易出错。

  • 可以在全局的配置文件中设置别名,在mapper.xml配置文件中,输入映射和输出映射可以直接写类名
  • 配置文件

    1. <!-- 设置别名 -->
    2. <typeAliases>
    3. <!-- 只能给单个类进行设置别名 -->
    4. <!-- <typeAlias type="com.six.model.User" alias="User"/>-->
    5. <!-- 将整个包下面的类设置为别名 -->
    6. <package name="com.six.model"/>
    7. </typeAliases>

    5.4 自定义的POJO类(重点)

  • 问题:前面写的所有的映射关系和sql指令,全部是单表操作,并且每一个单表都有对应的model类,现在需要多张表同时进行查询、删除、更新等操作。特别是查询。

  • 现在有两张表,t_user和t_info,标结构如下:

image.png
image.png

  • 现在查询的要求:查询出性别为男,年龄为20的用户信息
  • sql查询语句:在查询的条件里面有两个条件,gender在t_user表中,age在t_info表中,返回的结果只用user数据。

    1. SELECT * FROM t_user AS u JOIN t_info AS i ON u.id=i.user_id
    2. WHERE gender = "男" AND age = 18
  • 自定义的pojo类

    1. /**
    2. *
    3. * @ClassName: CustomUser
    4. * @Description:扩展的用户类
    5. * @author Alon
    6. * @date 2020年11月20日 下午4:13:34
    7. *
    8. */
    9. public class CustomUser {
    10. private User user;
    11. private UserInfo userInfo;
    12. public User getUser() {
    13. return user;
    14. }
    15. public void setUser(User user) {
    16. this.user = user;
    17. }
    18. public UserInfo getUserInfo() {
    19. return userInfo;
    20. }
    21. public void setUserInfo(UserInfo userInfo) {
    22. this.userInfo = userInfo;
    23. }
    24. }
  • mapper接口

    1. /**
    2. *
    3. * @Title: queryUser
    4. * @Description:根据指定条件查询用户数据
    5. * @param @return
    6. * @param @throws Exception
    7. * @return User
    8. * @throws
    9. */
    10. public User queryUser(CustomUser customUser) throws Exception;
  • mapper.xml配置文件

    1. <!-- 查询语句:查询用户数据对象,
    2. 条件1:用户的性别为男
    3. 条件2:用户的年龄只能是18岁
    4. -->
    5. <select id="queryUser" parameterType="CustomUser" resultType="User">
    6. SELECT * FROM t_user AS u JOIN t_info AS i ON u.id=i.user_id
    7. WHERE gender = #{user.gender} AND age = #{userInfo.age}
    8. </select>
  • 测试类

    1. /**
    2. * 根据条件查询指定用户的数据
    3. */
    4. @Override
    5. public User queryUser(CustomUser customUser) throws Exception {
    6. //获取mapper对象
    7. UserMapper mapper = session.getMapper(UserMapper.class);
    8. User user = mapper.queryUser(customUser);
    9. System.out.println(user);
    10. return null;
    11. }
    12. public static void main(String[] args) throws Exception {
    13. CustomUser cu = new CustomUser();
    14. User user = new User();
    15. user.setGender("男");
    16. UserInfo userInfo = new UserInfo();
    17. userInfo.setAge(18);
    18. cu.setUser(user);
    19. cu.setUserInfo(userInfo);
    20. new UserDaoImpl().queryUser(cu);
    21. }

    5.5 输入映射和输出映射

  • 输入映射:parameterType和parameterMap,一般使用parameterType,输入映射的参数可以是哪些数据?

基本数据类型、包装类型、自定义的pojo类。

  • 输出映射:resultType和resultMap,一般在开发中使用resultMap,
    • resultType:可以使用自定义的pojo类,可以是简单数据类型(要保证该条sql语句查询的结果集只能有一行一列的结果),不能实现延迟加载。
    • resultMap:可以使用自定义的pojo类,可以使用一对一、一对多、多对多的级联查询,可以完成模型中属性名和数据库字段名不一致情况下使用。可以实现延迟加载。
  • resultMap入门编程(重点在明天)

    1. <!-- 定义一个resultMap
    2. id:必须和引用该resultMap的查询语句里面resultMap值一致
    3. type:数据类型,表示的要映射到哪个模型类,可以使用别名
    4. -->
    5. <resultMap type="User" id="queryUserOneMap">
    6. <!-- id标签
    7. 对应数据库表中主键的映射关系
    8. column:表中主键字段的名称
    9. property:对应模型中的属性名
    10. -->
    11. <id column="id" property="id"/>
    12. <!-- result
    13. 对应数据库表中除了主键之外的其他字段
    14. column:表中其他字段的名称
    15. property:对应模型中的属性名
    16. -->
    17. <result column="user_name" property="userName"/>
    18. <result column="gender" property="gender"/>
    19. <result column="birthday" property="birthday"/>
    20. <result column="address" property="address"/>
    21. </resultMap>
    22. <!-- 查询单个用户,根据id -->
    23. <select id="queryUserOne" parameterType="int" resultMap="queryUserOneMap">
    24. SELECT id,user_name,birthday,gender,address FROM t_user WHERE id = #{id}
    25. </select>

    5.6 sql片段

  • 在实际开发中,很多业务需求,需要做sql拼接,一般用在高级查询。作用是:按需拼接sql语句,语句都写在配置文件中,可以进行参数的判断,如果该参数不为null,有值的情况下,将sql语句加上去。

  • 举例:

    1. 一条完整的sql语句
    2. SELECT * FROM t_user WHERE gender = "男" AND address = "重庆"
    3. 可以使用sql片段进行拼接
    4. 主查询语句
    5. SELECT 字段 FROM t_user
    6. 1、第一个判断:如果gender值不为null
    7. WHERE AND gender = "男"
    8. 2、第二个判断:如果address不为null
    9. AND address = "重庆"
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace = "com.six.mapper.UserMapper">
    6. <!-- 实现多条件查询sql语句 -->
    7. <select id="queryUser" parameterType="User" resultType="User">
    8. SELECT id,user_name,birthday,gender,address FROM t_user
    9. <where>
    10. <if test="gender != null and gender != ''">
    11. AND gender=#{gender}
    12. </if>
    13. <if test="address != null and address != ''">
    14. AND address = #{address}
    15. </if>
    16. </where>
    17. </select>
    18. </mapper>
  • 抽取之后的sql片段

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace = "com.six.mapper.UserMapper">
    6. <!-- 将sql片段进行抽取 -->
    7. <sql id="genderAndaddress">
    8. <if test="gender != null and gender != ''">
    9. AND gender=#{gender}
    10. </if>
    11. <if test="address != null and address != ''">
    12. AND address = #{address}
    13. </if>
    14. </sql>
    15. <!-- 实现多条件查询sql语句 -->
    16. <select id="queryUser" parameterType="User" resultType="User">
    17. SELECT id,user_name,birthday,gender,address FROM t_user
    18. <where>
    19. <include refid="genderAndaddress"></include>
    20. </where>
    21. </select>
    22. </mapper>

    5.7 mybatis中的映射关系

    5.7.1 一对一的映射关系

  • 举例:查询订单中关联的用户信息

  • 主表:订单表t_order,附表:用户表t_user

方式1:结果集用resultType来接收
需要生成一个新的模型类,以主表为父类,将附表对应模型类中的字段重新编写一次,生成为新的模型扩展类(OrderCustom)

  • 生成的扩展模型类 ```java public class OrderCustom extends Order{

    private String user_name; private String gender; private String birthday; private String address; public String getUser_name() { return user_name; } public void setUser_name(String user_name) { this.user_name = user_name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getBirthday() { return birthday; } public void setBirthday(String birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return “OrderCustom [user_name=” + user_name + “, gender=” + gender + “, birthday=” + birthday + “, address=”

    1. + address + ", getId()=" + getId() + ", getUser_id()=" + getUser_id() + ", getCreate_time()="
    2. + getCreate_time() + ", getNote()=" + getNote() + "]";

    }

}

  1. - mapper接口
  2. ```java
  3. public List<OrderCustom> queryOrderAndUser() throws Exception;
  • mapper.xml配置文件

    1. <!-- 查询订单关联的用户信息 -->
    2. <select id="queryOrderAndUser" resultType="OrderCustom">
    3. SELECT
    4. u.id,
    5. user_id,
    6. create_time,
    7. note,
    8. user_name,
    9. birthday,
    10. gender,
    11. address
    12. FROM t_order AS o JOIN t_user AS u
    13. ON o.user_id = u.id;
    14. </select>
  • 测试类

    1. @Override
    2. public List<OrderCustom> queryOrderAndUser() throws Exception {
    3. List<OrderCustom> list = mapper.queryOrderAndUser();
    4. System.out.println(list);
    5. return null;
    6. }

方式2:结果集用resultMap来接收
需要解决的问题:需要用一个模型类来接受两个表中查询的结果集,而且要以主表为返回结果集
解决方式:在主表对应的模型中引入附表对应的模型对象

  • 模型类:主要在Order模型中引入了User对象 ```java public class Order { private int id; private int user_id; private String create_time; private String note; private User user;

    public User getUser() { return user; } public void setUser(User user) { this.user = user; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUser_id() { return user_id; } public void setUser_id(int user_id) { this.user_id = user_id; } public String getCreate_time() { return create_time; } public void setCreate_time(String create_time) { this.create_time = create_time; } public String getNote() { return note; } public void setNote(String note) { this.note = note; }

    @Override public String toString() { return “Order [id=” + id + “, user_id=” + user_id + “, create_time=” + create_time + “, note=” + note

    1. + ", user=" + user + "]";

    } }

  1. - 接口
  2. ```java
  3. public List<Order> queryOrderAndUser2()throws Exception;
  • mapper.xml配置文件

    1. <!-- 自定义resultMap
    2. type:要映射的模型对象,可以使用别名
    3. -->
    4. <resultMap type="Order" id="queryOrderAndUser2Map">
    5. <!-- 主表order映射关系写好
    6. id:主表中主键的关系映射(关系:数据库字段和模型类中的属性名)
    7. column:数据库中(查询结果集,如果取了别名是生效)的字段
    8. property:模型中的属性名
    9. -->
    10. <id column="id" property="id"/>
    11. <!-- 如果模型中的属性名和查询结果集中的字段名一致,可以省略
    12. result:表示主表中除了主键之外的普通字段映射关系
    13. column:数据库中(查询结果集,如果取了别名是生效)的字段
    14. property:模型中的属性名
    15. -->
    16. <result column="user_id" property="user_id"/>
    17. <result column="create_time" property="create_time"/>
    18. <result column="note" property="note"/>
    19. <!-- 配置在order模型中引入的user对象关系映射:一对一
    20. 使用association标签完成一对一的关系映射
    21. property:在主表中声明的对象名称,user
    22. javaType:对象对应的模型类,可以使用别名
    23. -->
    24. <association property="user" javaType="User">
    25. <!-- 主键配置 -->
    26. <id column="user_id" property="id"/>
    27. <!-- 其他普通字段映射 -->
    28. <result column="user_name" property="user_name"/>
    29. <result column="birthday" property="birthday"/>
    30. <result column="gender" property="gender"/>
    31. <result column="address" property="address"/>
    32. </association>
    33. </resultMap>
    34. <!-- 查询所有订单对应的用户信息,返回结果用resultMap方式,
    35. 如果使用resultMap进行返回集映射,必须单独配置一个resultMap标签
    36. -->
    37. <select id="queryOrderAndUser2" resultMap="queryOrderAndUser2Map">
    38. SELECT
    39. u.id,
    40. user_id,
    41. create_time,
    42. note,
    43. user_name,
    44. birthday,
    45. gender,
    46. address
    47. FROM t_order AS o JOIN t_user AS u
    48. ON o.user_id = u.id;
    49. </select>
  • 测试类

    1. public List<Order> queryOrderAndUser2() throws Exception {
    2. List<Order> list = mapper.queryOrderAndUser2();
    3. System.out.println(list);
    4. return null;
    5. }

    5.7.2 一对多关系映射

    举例:查询订单和订单明细及用户数据
    主表:订单表t_order,
    附表:订单明细表t_detail和用户表t_user
    模型的操作:
    在Order类中添加明细的声明的对象(一对多),再添加用户声明的对象。

  • 编写SQL语句

    1. SELECT
    2. o.id,
    3. user_id,
    4. o.create_time AS order_create_time,
    5. note,
    6. user_name,
    7. birthday,
    8. gender,
    9. address,
    10. d.id AS detail_id,
    11. product_id,
    12. d.create_time AS detail_create_time,
    13. items_id,
    14. number
    15. FROM t_order AS o JOIN t_user AS u
    16. ON o.user_id = u.id
    17. JOIN t_detail AS d ON o.id = d.order_id;
  • 修改的Order类模型

    1. public class Order {
    2. private int id;
    3. private int user_id;
    4. private String create_time;
    5. private String note;
    6. private User user;
    7. private List<Detail> detailList;
    8. public List<Detail> getDetailList() {
    9. return detailList;
    10. }
    11. public void setDetailList(List<Detail> detailList) {
    12. this.detailList = detailList;
    13. }
    14. public User getUser() {
    15. return user;
    16. }
    17. public void setUser(User user) {
    18. this.user = user;
    19. }
    20. public int getId() {
    21. return id;
    22. }
    23. public void setId(int id) {
    24. this.id = id;
    25. }
    26. public int getUser_id() {
    27. return user_id;
    28. }
    29. public void setUser_id(int user_id) {
    30. this.user_id = user_id;
    31. }
    32. public String getCreate_time() {
    33. return create_time;
    34. }
    35. public void setCreate_time(String create_time) {
    36. this.create_time = create_time;
    37. }
    38. public String getNote() {
    39. return note;
    40. }
    41. public void setNote(String note) {
    42. this.note = note;
    43. }
    44. @Override
    45. public String toString() {
    46. return "Order [id=" + id + ", user_id=" + user_id + ", create_time=" + create_time + ", note=" + note
    47. + ", user=" + user + ", detailList=" + detailList + "]";
    48. }
    49. }
  • mapper接口

    1. public List<Order> queryOrderAndDetail() throws Exception;
  • mapper.xml配置文件 ```xml

  1. <!-- 查询订单和订单明细及用户信息,只能选择resultMap -->
  2. <select id="queryOrderAndDetail" resultMap="queryOrderAndDetailMap">
  3. SELECT
  4. o.id,
  5. user_id,
  6. o.create_time AS order_create_time,
  7. note,
  8. user_name,
  9. birthday,
  10. gender,
  11. address,
  12. d.id AS detail_id,
  13. product_id,
  14. d.create_time AS detail_create_time,
  15. items_id,
  16. number
  17. FROM t_order AS o JOIN t_user AS u
  18. ON o.user_id = u.id
  19. JOIN t_detail AS d ON o.id = d.order_id;
  20. </select>

  1. - 测试类
  2. ```java
  3. @Override
  4. public List<Order> queryOrderAndDetail() throws Exception {
  5. List<Order> list = mapper.queryOrderAndDetail();
  6. System.out.println(list);
  7. return null;
  8. }

5.7.3 多对多的映射关系

举例:查询用户购买的商品信息,用户和商品表之间没有直接的关联信息,在用户表中添加订单对象(声明成集合)、在订单模型中添加订单明细对象(声明成集合)、在订单明细中声明商品对象(单个对象)
主表:用户表t_user
附表:订单表t_order,明细表:t_detail,商品表:t_product

  • 改变后的模型类

用户模型:

  1. private int id;
  2. private String user_name;
  3. private String gender;
  4. private String birthday;
  5. private String address;
  6. //声明订单
  7. private List<Order> listOrder;

订单模型

  1. private int id;
  2. private int user_id;
  3. private String create_time;
  4. private String note;
  5. //声明订单明细
  6. private List<Detail> detailList;

订单明细模型

  1. private int id;
  2. private int order_id;
  3. private int product_id;
  4. private String create_time;
  5. private int items_id;
  6. private int number;
  7. //声明一个商品
  8. private Product product;
  • 编写sql语句

    1. SELECT
    2. u.id,
    3. user_name,
    4. birthday,
    5. gender,
    6. address,
    7. o.id AS order_id,
    8. o.create_time AS order_create_time,
    9. note,
    10. d.id AS detail_id,
    11. product_id,
    12. d.create_time AS detail_create_time,
    13. items_id,
    14. d.number AS detail_number,
    15. product_name,
    16. p.number AS product_number,
    17. add_time,
    18. price
    19. FROM t_user AS u JOIN t_order o
    20. ON u.id = o.user_id
    21. JOIN t_detail AS d ON o.id = d.order_id
    22. JOIN t_product AS p ON d.product_id = p.id
  • mapper接口

    1. public List<User> queryUserAndProduct(int userId) throws Exception;
  • mapper.xml配置文件

    1. <!-- 自定义resultMpa -->
    2. <resultMap type="User" id="queryUserAndProductMap">
    3. <!-- 主表中的映射关系 -->
    4. <id column="id" property="id"/>
    5. <result column="user_name" property="user_name"/>
    6. <result column="birthday" property="birthday"/>
    7. <result column="gender" property="gender"/>
    8. <result column="address" property="address"/>
    9. <!-- 配置user模型中对应的订单对象,一对多 -->
    10. <collection property="listOrder" ofType="Order">
    11. <id column="order_id" property="id"/>
    12. <result column="order_create_time" property="create_time"/>
    13. <result column="note" property="note"/>
    14. <!-- 配置Order模型中对应的订单明细对象,一对多 -->
    15. <collection property="detailList" ofType="Detail">
    16. <id column="detail_id" property="id"/>
    17. <result column="product_id" property="product_id"/>
    18. <result column="detail_create_time" property="create_time"/>
    19. <result column="items_id" property="items_id"/>
    20. <result column="detail_number" property="number"/>
    21. <!-- 配置在Detail模型中对应的商品对象,一对一 -->
    22. <association property="product" javaType="Product">
    23. <id column="product_id" property="id"/>
    24. <result column="product_name" property="product_name"/>
    25. <result column="product_number" property="number"/>
    26. <result column="add_time" property="add_time"/>
    27. <result column="price" property="price"/>
    28. </association>
    29. </collection>
    30. </collection>
    31. </resultMap>
    32. <!-- 查询用户购买了哪些商品 -->
    33. <select id="queryUserAndProduct" parameterType="java.lang.Integer" resultMap="queryUserAndProductMap">
    34. SELECT
    35. u.id,
    36. user_name,
    37. birthday,
    38. gender,
    39. address,
    40. o.id AS order_id,
    41. o.create_time AS order_create_time,
    42. note,
    43. d.id AS detail_id,
    44. product_id,
    45. d.create_time AS detail_create_time,
    46. items_id,
    47. d.number AS detail_number,
    48. product_name,
    49. p.number AS product_number,
    50. add_time,
    51. price
    52. FROM t_user AS u JOIN t_order o
    53. ON u.id = o.user_id
    54. JOIN t_detail AS d ON o.id = d.order_id
    55. JOIN t_product AS p ON d.product_id = p.id
    56. WHERE u.id = #{userId}
    57. </select>
  • 测试类

    1. @Override
    2. public List<User> queryUserAndProduct(int userId) throws Exception {
    3. List<User> list = mapper.queryUserAndProduct(userId);
    4. System.out.println(list);
    5. return null;
    6. }

    六、延迟加载

  • 什么是延迟加载?

一般也称为按需加载,复杂的语句都是有结构层次的,如果是多表查询,可以先查询主表中的内容,只有在业务逻辑中使用了附表中数据的时候,才会继续执行查询附表中的内容。

  • 举例说明,查询订单对应的用户数据

第一层查询:只查询订单数据即可
第二层查询:用第一层查询结果里面user_id作为条件去筛选用户表中的数据

  • 要使用延迟加载的条件

1、只能使用resultMap,在resultMap里面的association和collection两个标签才具备延迟加载的功能。
2、还需要在全局配置文件中,将延迟加载打开,需要使用到全局配置文件中的标签进行设置。

  1. <settings>
  2. <setting name="lazyLoadingEnabled" value="true"/>
  3. <setting name="aggressiveLazyLoading" value="false"/>
  4. </settings>
  • mapper.xml配置文件

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.six.mapper.OrderMapper">
    6. <!-- 查询用户语句,根据user_id和订单表关联
    7. id:如果是延迟加载需要的查询语句,必须和association或者collection标签中的select值一致
    8. -->
    9. <select id="queryUserById" parameterType="java.lang.Integer" resultType="User">
    10. SELECT id,user_name,birthday,gender,address FROM t_user WHERE id = #{id}
    11. </select>
    12. <!-- 编写自定义的resultMap -->
    13. <resultMap type="Order" id="queryOrderAndUserMap">
    14. <!-- 关联映射主表数据 -->
    15. <id column="id" property="id"/>
    16. <result column="user_id" property="user_id"/>
    17. <result column="create_time" property="create_time"/>
    18. <result column="note" property="note"/>
    19. <!-- 关联user映射对象关系
    20. property:在order类中声明的用户对象名
    21. javaType:user对象对应的model全路径,可以使用别名
    22. select:调用另外一个select标签中的代码(调用查询用户数据的select),名字自定义
    23. column:要将主表查询结果集中哪个字段的值作为下一条查询语句的条件
    24. -->
    25. <association property="user" javaType="User" select="queryUserById" column="user_id"></association>
    26. </resultMap>
    27. <!-- 查询语句:查询订单所对应的用数据,要使用延迟加载 -->
    28. <select id="queryOrderAndUser" resultMap="queryOrderAndUserMap">
    29. SELECT id,user_id,create_time,note FROM t_order
    30. </select>
    31. </mapper>

    七、mybatis缓存机制

  • 概念:在mybatis中,缓存机制的作用是提高查询效率,模式:第一次查询去数据库中查询,将查询的结果集保存在对应的缓存内存中,第二次相同的查询语句,则直接在内存中拿取数据。

    7.1 一级缓存

  • 概念:存储的位置是在session层面,一次会话会产生独立的缓存空间。中间如果出现了提交事务的操作,那么会清空缓存中的内容。

  • 在mybatis中以及缓存默认开始。

    7.2 二级缓存

  • 概念:存储的位置是在Mapper层面,通过每一个mapper的namespace进行识别,模式:第一次查询去数据库中查询,会将结果保存在对应的缓存内存中,第二次有相同的查询语句,则直接在内存中拿数据。

  • 在mybatis中二级缓存需要手动开启。需要在全局的配置文件中用settings标签设置,也需要在对应的mapper中添加标签。
  • 需要在对映射的模型类实现序列化
    1. //在SqlMapConfig.xml配置文件中
    2. <settings>
    3. <!-- 开启二级缓存 -->
    4. <setting name="cacheEnabled" value="true"/>
    5. </settings>
    6. //对应的mapper.xml文件中,在mapper的第一行
    7. <cache />