Mybatis入门(复习)

mybatis

https://mybatis.org/mybatis-3/zh/getting-started.html

什么是Mybatis?

  • MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

优点

  • **简单易学:本身就很小且简单。**没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的orm字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供xml标签,支持编写动态sql。

入门

使用

普通的java的项目只需要将mybatis-xxx.jar文件引入项目即可。

Maven构建的项目,则将jar包依赖引入pom.xml文件即可:

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

第一个Mybatis程序

思路:搭建环境–> 导入mybatis–>编写代码–>测试!

1.搭建环境

创建数据库表格

CREATE DATABASE `mybatis`;
USE `mybatis`;
CREATE TABLE `user` (
`id` INT ( 11 ) NOT NULL PRIMARY KEY,
`name` VARCHAR ( 255 ) DEFAULT NULL,
`pwd` VARCHAR ( 255 ) DEFAULT NULL,
`age` INT ( 200 ) NOT NULL DEFAULT 0 
) ENGINE = INNODB DEFAULT CHARACTER 
SET = utf8;

INSERT INTO `user` ( `name`, `pwd`, `age` )
VALUES
	( '张三', '123', 18 ),
	( '李四', '456', 24 ),
	( '王五', '789', 20 ),
	( '无极', '258', 30 ),
	( '邓泉', '147', 16 );

新建一个maven项目

2.导入依赖

<!--1. mysql驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
</dependency>
<!--2. mybatis框架-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.4</version>
</dependency>

<!--3. junit测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13</version>
</dependency>
<!--4. lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

3.编写代码

编写核心配置文件 通过这个方法可以获取sqlsession对象,从而操作mapper接口

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    private static String resource = "mybatis-config.xml";
    static {
        try {
            //使用mybatis第一步:获取sqlsessionFactory工厂对象
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取sqlsession对象
     * @return
     */
    public static SqlSession getSqlSession(){
         return sqlSessionFactory.openSession();
    }
}

创建user对象

package com.potato.pojo;

import lombok.Data;
/**
 * \* Created with IntelliJ IDEA.
 * \* User: 一颗小土豆
 * \* Date: 2020/5/30
 * \* Time: 15:40
 */
@Data //使用lombok注解
public class User {
    private Integer id;
    private String name;
    private String pwd;
    private Integer age;
}

创建mapper.java接口和mapper.xml映射文件

mapper.java

public interface UserMapper {
    //查询所有的user
    List<User> getAll();
}

mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间 值为 对应的mapper接口全路径名-->
<mapper namespace="com.potato.mapper.UserMapper">
    <!--id 唯一标识符 对应着mapper接口的方法名  resultType输出类型User-->
    <select id="getAll" resultType="com.potato.pojo.User">
      select * from `user`
    </select>
</mapper>

在mybatis-config.xml中 配置mapper.xml

<!--我们使用包扫描  mapper的包扫描-->
<mappers>
    <!--<mapper resource="com/potato/mapper/UserMapper.xml"></mapper> 单个注入-->
    <package name="com.potato.mapper"></package>
</mappers>

在pom.xml中配置resources的文件输出,如果不配置会导致mapper.xml编译不到,导致找不到mapper.xml该文件

 <!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>

            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

在这里插入图片描述

4.测试

public class UserMapperTest {
    @Test
    public void test1(){
        //获得SqlSession
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //获得mapper接口对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //执行查询方法
        List<User> userList = mapper.getAll();
        //打印输出
        userList.forEach(System.out::println);
        
        /*结果:
          * User(id=1, name=张三, pwd=123, age=18)
          * User(id=2, name=李四, pwd=456, age=24)
          * User(id=3, name=王五, pwd=789, age=20)
          * User(id=4, name=无极, pwd=258, age=30)
          * User(id=5, name=邓泉, pwd=147, age=16)
        */
    }
}

由浅入深

CRUD测试

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">
<!--namespace命名空间 值为 对应的mapper接口全路径名-->
<mapper namespace="com.potato.mapper.UserMapper">

    <!--id 唯一标识符 对应着mapper接口的方法名  resultType输出类型User-->
    <select id="getAll" resultType="com.potato.pojo.User">
      select * from `user`
    </select>

    <insert id="addUser" parameterType="com.potato.pojo.User">
        insert into user (name, pwd, age) values (#{name},#{pwd},#{age});
    </insert>

    <delete id="deleteUserById" parameterType="int">
        delete from user where id = #{id}
    </delete>

    <update id="updateUserById" parameterType="map">
        update user set name = #{name} ,pwd = #{pwd}  where id = #{id};
    </update>

</mapper>

UserMapper.java

package com.potato.mapper;
import com.potato.pojo.User;
import java.util.List;
import java.util.Map;
/**
 * @description
 * @author: 土豆
 * @create: 2020-05-30 15:37
 **/
public interface UserMapper {
    List<User> getAll();

    int addUser(User user);

    int deleteUserById(Integer id);

    int updateUserById(Map map);
}

UserMapperTest.java

package com.potato.mapper;

import com.potato.pojo.User;
import com.potato.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
public class UserMapperTest {
    //查
    @Test
    public void selectUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getAll();
        userList.forEach(System.out::println);
        sqlSession.close();
    }

    //增
    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setName("王二小");
        user.setPwd("456");
        user.setAge(18);
        int num = mapper.addUser(user);
        System.out.println(num);
        sqlSession.commit();
        sqlSession.close();
    }

    //删
    @Test
    public void deleteUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int num = mapper.deleteUserById(4);
        System.out.println(num);
        sqlSession.commit();
        sqlSession.close();
    }

    //删
    @Test
    public void updateUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("id",8);
        map.put("name","胡爱国");
        map.put("pwd","qwer");
        map.put("age",18);
        int num = mapper.updateUserById(map);
        System.out.println(num);
        sqlSession.commit();
        sqlSession.close();
    }
}

模糊查询 like语句

第一种:使用$ 字符串拼接的方式,存在sql注入风险的方法:

    <!-- 模糊查询 有sql注入风险 -->
    <select id="likeUserByNameToString" parameterType="String" resultType="com.potato.pojo.User">
        select * from user alias where alias.name like '%${name}%';
    </select>

测试

    //模糊查询 有sql注入风险
    @Test
    public void likeUserByNameToString(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //我们拼接一sql 可以看结果:
        List<User> userList = mapper.likeUserByNameToString("二%' or id like '%1");
        userList.forEach(System.out::println);
        sqlSession.close();
    }
	/* 结果不但把 name 中有二的数据查出来了,还把id为1开头的查出来了,这样做极其不安全
	sql: select * from user where name like '%二%' or id like '%1%';
	==> User(id=1, name=张三, pwd=123, age=18)
	==> User(id=9, name=王二小, pwd=456, age=18)
	*/
}

第二种:使用# 的方式,但是要记得#方法不能字符串拼接 也就是不能为: “%#{name}%”

    <!-- 模糊查询 有sql注入风险 -->
    <select id="likeUserByNameToString" parameterType="String" resultType="com.potato.pojo.User">
        select * from user alias where alias.name like #{name};
    </select>

测试

  //模糊查询 倒是没有sql注入风险,但是麻烦
    @Test
    public void likeUserByNameToString2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.likeUserByNameToString2("%二%");
        userList.forEach(System.out::println);
        sqlSession.close();
    }
	/* 结果查询出来的没问题,但是很麻烦,而且总不能一直手动拼接吧
	sql: select * from user alias where alias.name like ?; 
	==> Parameters: %二%(String)
	==> User(id=9, name=王二小, pwd=456, age=18)
	*/
}

第三种方法:使用mysql 的拼接方法 CONCAT('%','二','%'),这种方法是最好的解决方案。

    <!-- 模糊查询 使用concat方法 我认为是挺好的解决方案 -->
    <select id="likeUserByNameToString3" parameterType="String" resultType="com.potato.pojo.User">
        select * from user alias where alias.name like CONCAT('%',#{name},'%');
    </select>

测试

 //模糊查询
    @Test
    public void likeUserByNameToString3(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.likeUserByNameToString3("二");
        userList.forEach(System.out::println);
        sqlSession.close();
    }

mybatis配置文件的详解 mybatis-config.xml

mybatis-config.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>
    <!--引入外部db配置文件-->
    <properties resource="db.properties" />

    <settings>
        <!-- 控制台打印sql语句 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <environments default="development">
        <!--这里可以配置多个数据源 通过default 来制定使用哪一个-->
        <!--开发环境-->
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
        <!--生产环境-->
        <environment id="production">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--mapper的包扫描-->
    <mappers>
        <!--<mapper resource="com/potato/mapper/UserMapper.xml"></mapper>-->
        <package name="com.potato.mapper"></package>
    </mappers>
</configuration>

1、配置文件的配置要遵循一下顺序

properties ==> settings ==> typeAliases ==> typeHandlers ==> objectFactory

==> objectWrapperFactory ==> reflectorFactory ==> plugins ==> environments

==> databaseIdProvider ==> mappers

2、外部properties文件的引入

例子🌰:按照配置顺序,引入数据源的配置,在environments数据源使用 ${xx}引入即可

 <!--引入外部db配置文件-->
 <properties resource="db.properties" />

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://39.105.27.58/mybatis?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT
username=root
password=lovehxp521..

3、实体类别名

我们在mapper.xml文件中 返回类型每次都是用全路径名不不方便,导致代码的冗余。所以我们配置实体类别名

第一种方式:配置单个类

<typeAliases>
    <!--配置单个类-->
    <typeAlias alias="User" type="com.potato.pojo.User"/>
</typeAliases>

第二种方式:包扫描

<typeAliases>
    <!--包扫描-->
    <package name="com.potato.pojo"></package>>
</typeAliases>

mapper.xml

<!--返回类型可以使用别名 默认以类名开头字母小写-->
<select id="getAll" resultType="user">
    select * from `user`
</select>

第三种:使用@Alias注解

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

三种方式都可以,第二种我们使用的最多,如果想自定义别名时可以使用注解,或者在配置文件中,单个配置

4、设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

官网:https://mybatis.org/mybatis-3/zh/configuration.html#settings

比较重要的几个:

  1. cacheEnabled 缓存 默认:true
  2. lazyLoadingEnabled 懒加载 默认:false
  3. mapUnderscoreToCamelCase 驼峰命名 默认:false

5、映射器(mappers)

作用:将接口方法和sql语句绑定

实现方式有四种:

常用的有2种(不常用的就不在解释,官网有):

  1. 通过映射文件的资源引用

        <mappers>
    		<mapper resource="com/potato/mapper/UserMapper.xml"></mapper>
        </mappers>
    
  2. 通过包扫描的方式,注意通过包扫描的方式必须 接口xml映射文件必须同包且同名

        <mappers>
            <package name="com.potato.mapper"></package>
        </mappers>
    

作用域(Scope)和生命周期

作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。


SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式

SqlSession

每个线程都应该有它自己的 SqlSession实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要。

XML 映射器


MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • sql – 可被其它语句引用的可重用语句块。
  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • select – 映射查询语句。
1、增删改查在上述的CRUD操作中我们已经举过例子了,这里就不再解释
2、我们提出问题,如果当实体类的属性名数据库的字段名不一致时,结果映射不上去怎么解决?

创建一个新的表:book

USE mybatis;
CREATE TABLE `book`(
	id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
	book_name VARCHAR(255) NOT NULL,
	book_pic DOUBLE NOT NULL DEFAULT 0,
	book_author VARCHAR(255) NOT NULL DEFAULT '未知作者'
)ENGINE = INNODB DEFAULT CHARACTER SET = utf8;

INSERT INTO `book`(book_name,book_pic,book_author) 
VALUES
('海王是怎么炼成的',99.5,'小黄'),
('渣男必备攻略',56.5,'小绿'),
('舔狗从入门到应有尽有!',180.0,'小黄·终极之作');

创建相应的实体类,并且使其价格字段和数据库不一致

@Data
public class Book {
    private Integer id;
    private String bookName;
    private Double showPic;//showPic 这里和数据库的不一致,其他的因为开启了驼峰命名所以是可以映射上去的
    private String bookAuthor;
}
失败的案例:

bookMapper.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.potato.mapper.BookMapper">
    <select id="getAll" resultType="book">
        select id, book_name, book_pic, book_author from book
    </select>
</mapper>

bookMapper.java

public interface BookMapper {
    List<Book> getAll();
}

测试-结果:

public class BookMapperTest {
    @Test
    public void getAll(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BookMapper mapper = sqlSession.getMapper(BookMapper.class);
        List<Book> bookList = mapper.getAll();
        bookList.forEach(System.out::println);
        sqlSession.close();
    }
    /*
    ==>  Preparing: select id, book_name, book_pic, book_author from book 
    ==> Parameters: 
    <==    Columns: id, book_name, book_pic, book_author
    <==        Row: 1, 朝花夕拾, 59.6, 鲁迅
    <==        Row: 2, 浮生六记, 49.5, 沈复
    <==        Row: 3, 济南的冬天, 48, 老舍
    <==      Total: 3
    Book(id=1, bookName=朝花夕拾, showPic=null, bookAuthor=鲁迅)
    Book(id=2, bookName=浮生六记, showPic=null, bookAuthor=沈复)
    Book(id=3, bookName=济南的冬天, showPic=null, bookAuthor=老舍)
    我们发现showPic并没有映射上结果,结果其实是查询出来的
    */
}
方案一:给sql 字段 添加别名

MySQL的结果集映射时按照输出的列名进行映射的,我们可以通过给sql 字段 添加别名进行映射。

<!-- 添加别名 as showPic -->
<select id="getAll" resultType="book">
    select id, book_name, book_pic as showPic, book_author from book
</select>

再次测试,不需要修改java代码

public class BookMapperTest {
    @Test
    public void getAll(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BookMapper mapper = sqlSession.getMapper(BookMapper.class);
        List<Book> bookList = mapper.getAll();
        bookList.forEach(System.out::println);
        sqlSession.close();
    }
    /*
    ==>  Preparing: select id, book_name, book_pic as showPic, book_author from book 
    ==> Parameters: 
    <==    Columns: id, book_name, showPic, book_author
    <==        Row: 1, 朝花夕拾, 59.6, 鲁迅
    <==        Row: 2, 浮生六记, 49.5, 沈复
    <==        Row: 3, 济南的冬天, 48, 老舍
    <==      Total: 3
    Book(id=1, bookName=朝花夕拾, showPic=59.6, bookAuthor=鲁迅)
    Book(id=2, bookName=浮生六记, showPic=49.5, bookAuthor=沈复)
    Book(id=3, bookName=济南的冬天, showPic=48.0, bookAuthor=老舍)
    这两样是可以的,但是这样做如果字段多了很麻烦而且不太聪明。小学生都会的东西
    */
}
方案二:结果映射(resultMap)
  • constructor

    - 用于在实例化类时,注入结果到构造方法中

    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

  • result – 注入到字段或 JavaBean 属性的普通结果

  • association

    – 一个复杂类型的关联;许多结果将包装成这种类型

    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection

    – 一个复杂类型的集合

    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator

    – 使用结果值来决定使用哪个

    resultMap

    • case

      – 基于某些值的结果映射

      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

1、修改mapper.xml,将返回结果改为 resultMap

<?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.potato.mapper.BookMapper">
    <resultMap id="bookMap" type="book">
        <!--column 数据库字段名 property类属性名-->
        <!--
            id – 一个 主键ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
            result – 注入到字段或 JavaBean 属性的普通结果
        -->
        <id column="id" property="id"></id>
        <result column="book_name" property="bookName"></result>
        <result column="book_pic" property="showPic"></result>
        <result column="book_author" property="bookAuthor"></result>
    </resultMap>
    <select id="getAll" resultMap="bookMap">
        select id, book_name, book_pic, book_author from book
    </select>
</mapper>

测试-结果

public class BookMapperTest {
    @Test
    public void getAll(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BookMapper mapper = sqlSession.getMapper(BookMapper.class);
        List<Book> bookList = mapper.getAll();
        bookList.forEach(System.out::println);
        sqlSession.close();
    }
    /*
    ==>  Preparing: select id, book_name, book_pic, book_author from book 
    ==> Parameters: 
    <==    Columns: id, book_name, book_pic, book_author
    <==        Row: 1, 朝花夕拾, 59.6, 鲁迅
    <==        Row: 2, 浮生六记, 49.5, 沈复
    <==        Row: 3, 济南的冬天, 48, 老舍
    <==      Total: 3
    Book(id=1, bookName=朝花夕拾, showPic=59.6, bookAuthor=鲁迅)
    Book(id=2, bookName=浮生六记, showPic=49.5, bookAuthor=沈复)
    Book(id=3, bookName=济南的冬天, showPic=48.0, bookAuthor=老舍)
    结果还是能映射上去,这样一个简单的映射就完成了
    */
}
3、一对一和一对多的结果集映射

创建一个老婆表,咳咳,有缺老婆的和我说,给你们添加一个

USE mybatis;
CREATE TABLE `wife` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`name` VARCHAR ( 255 ) CHARACTER 
SET utf8 COLLATE utf8_bin NOT NULL,
`age` INT ( 200 ) NULL DEFAULT 0,
`like` VARCHAR ( 255 ) CHARACTER 
SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`user_id` INT ( 11 ) NOT NULL,
PRIMARY KEY ( `id` ) USING BTREE,
INDEX `user_id` ( `user_id` ) USING BTREE,
CONSTRAINT `user_id` FOREIGN KEY ( `user_id` ) REFERENCES `mybatis`.`user` ( `id` ) ON DELETE NO ACTION ON UPDATE NO ACTION 
) ENGINE = INNODB DEFAULT CHARACTER 
SET = utf8;

INSERT INTO `wife`
VALUES
	( 1, '翠花', 18, '钻戒', 1 ),
	( 2, '王大宝', 32, '金子', 2 ),
	( 3, '郝美丽', 22, '跑车', 3 ),
	( 5, '胡丽丽', 22, '花', 2 );
3.1.一对一
问题:假设每个用户都有一个老婆(反正中国只有一个),我们在查询用户信息的时候也查询出他老婆的信息。怎么做?
方案一:使用简单的java语句通过结果查询。

我们通过第一次查询用户的信息,然后在通过用户的字段再去查询他老婆的数据。

缺点:多次查询,性能差,非常不推荐。不做案例

方案二使用mysql嵌套查询

修改实体类,在不改变原有的代码下添加一个老婆属性

@Data
public class User {
    private Integer id;
    private String name;
    private String pwd;
    private Integer age;

    //包装类 存放自己的老婆
    private Wife wife;
}

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">
<!--namespace命名空间 值为 对应的mapper接口全路径名-->
<mapper namespace="com.potato.mapper.UserMapper">

    <resultMap id="UserAndWifeMap" type="user">
        <id column="id" property="id" />
        <result column="name" property="name"/>
        <result column="pwd" property="pwd"/>
        <result column="age" property="age"/>
        <association property="wife" javaType="wife">
            <id column="w_id" property="id"/>
            <result column="w_name" property="name"/>
            <result column="w_age" property="age"/>
            <result column="w_like" property="like"/>
            <result column="u_id" property="userId"/>
        </association>
    </resultMap>

    <!-- 左连接 -->
    <select id="getUserAndWife" resultMap="UserAndWifeMap">
        SELECT
            u.id,
            u.`name`,
            u.pwd,
            u.age,
            w.id AS w_id,
            w.`name` w_name,
            w.age w_age,
            w.`like` w_like,
            w.user_id u_id
        FROM
            `user` u
            LEFT JOIN `wife` w
        ON
            u.id = w.user_id
    </select>
</mapper>

测试类

    //查询用户和他老婆,左连接
    @Test
    public void selectUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserAndWife();
        userList.forEach(System.out::println);
        sqlSession.close();
    }

	/* 可以看出,使用左连接查询,左表中的数据都查出来了,和它相关的 wife表字段也都查询出来
	User(id=1, name=张三, pwd=123, age=18, wife=Wife(id=1, name=翠花, age=18, like=钻戒, userId=1), likeBooks=null)
    User(id=2, name=李四, pwd=456, age=24, wife=Wife(id=2, name=王大宝, age=32, like=金子, userId=2), likeBooks=null)
    User(id=3, name=王五, pwd=789, age=20, wife=Wife(id=3, name=郝美丽, age=22, like=跑车, userId=3), likeBooks=null)
    User(id=9, name=王二小, pwd=456, age=18, wife=Wife(id=5, name=胡丽丽, age=22, like=花, userId=9), likeBooks=null)
    User(id=5, name=邓泉, pwd=147, age=16, wife=null, likeBooks=null)
    User(id=8, name=胡爱国, pwd=qwer, age=28, wife=null, likeBooks=null)
    User(id=10, name=胡大鹏, pwd=258, age=55, wife=null, likeBooks=null)
	*/
3.2.一对多

查询用户喜欢的书有哪一些,我们创建一个用户书籍关联表

create table user_like_books
(
  id      int auto_increment
    primary key,
  user_id int not null,
  book_id int not null
)
  comment '用户喜欢的书籍 ';
--  随便导入一些数据

修改User实体类,添加一个Book的集合的成员变量

@Data
public class User {
    private Integer id;
    private String name;
    private String pwd;
    private Integer age;

    //包装类 存放自己的老婆
    private Wife wife;

    //包装类,每个用户都有积极喜欢的书籍,不只一本
    private List<Book> likeBooks;
}

写mapper接口和sql文件,还有最重要的映射 resultMap

<resultMap id="UserAndBooks" type="user">
    <id column="id" property="id" />
    <result column="name" property="name"/>
    <result column="pwd" property="pwd"/>
    <result column="age" property="age"/>
    <!--这里要注意
       一对一用 association 
       association 关联对象用 javaType="wife"

       一对多的集合使用 collection 很好理解 collection这个单词就是集合的意思
       collection关联对象用 ofType="book"
     -->
    <collection property="likeBooks" ofType="book">
        <id column="book_id" property="id" />
        <result column="book_name" property="bookName"/>
        <result column="book_pic" property="showPic"/>
        <result column="book_author" property="bookAuthor"/>
    </collection>
</resultMap>

<select id="selectUserAndBooks" resultMap="UserAndBooks">
    SELECT
    u.*,
    b.id book_id,
    b.book_name,
    b.book_pic,
    b.book_author
    FROM
    `user` u
    LEFT JOIN user_like_books ub ON u.id = ub.user_id
    LEFT JOIN book b ON ub.book_id = b.id
</select>

测试

//一对多 三表联查
@Test
public void selectUserAndBooks(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.selectUserAndBooks();
    userList.forEach(System.out::println);
    sqlSession.close();
}
/* 全部都查询出来了
User(id=1, name=张三, pwd=123, age=18, wife=null, likeBooks=[Book(id=1, bookName=朝花夕拾, showPic=59.6, bookAuthor=鲁迅), Book(id=2, bookName=浮生六记, showPic=49.5, bookAuthor=沈复)])
User(id=2, name=李四, pwd=456, age=24, wife=null, likeBooks=[Book(id=2, bookName=浮生六记, showPic=49.5, bookAuthor=沈复)])
User(id=3, name=王五, pwd=789, age=20, wife=null, likeBooks=[Book(id=3, bookName=济南的冬天, showPic=48.0, bookAuthor=老舍)])
User(id=5, name=邓泉, pwd=147, age=16, wife=null, likeBooks=[Book(id=4, bookName=海王是怎么炼成的, showPic=99.5, bookAuthor=小黄), Book(id=5, bookName=渣男必备攻略, showPic=56.5, bookAuthor=小绿)])
User(id=8, name=胡爱国, pwd=qwer, age=28, wife=null, likeBooks=[Book(id=5, bookName=渣男必备攻略, showPic=56.5, bookAuthor=小绿)])
User(id=9, name=王二小, pwd=456, age=18, wife=null, likeBooks=[Book(id=6, bookName=舔狗舔到最后应有尽有!, showPic=180.0, bookAuthor=小黄·终极之作)])
User(id=10, name=胡大鹏, pwd=258, age=55, wife=null, likeBooks=[])
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码神附体

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值