mybatis--1、入门基本使用

一、mybatis概述

1.1 为什么需要mybatis

传统通过jdbc连接数据库,缺点有很多:
1、每次加载连接都需要显式指定驱动名字
2、每次获取连接都需要指定连接信息,比如url、user、password等
3、sql语句和java代码耦合严重
4、参数类型需要手动判断
5、结果集中,列名、数据类型都需要自己手动判断
6、每次用完连接后都需要手动关闭

1.2 mybatis是什么

mybatis是基于jdbc封装的一个半orm持久化框架,支持sql查询,只要配置好连接信息,就无需我们自己去管理连接。根据根据需要采用xml配置或者注释的方式进行sql操作。将接口和pojo对象映射到数据库中。

1.3 mybatis中的两个概念

mybatis将映射数据表的实体类对象称为pojo对象,同时需要编写一个接口,接口里面定义了操作该表的一些方法。mybatis会根据映射关系操作数据库中的表。

1.4 mybatis的架构

mybatis--1、入门基本使用

二、mybatis的初步配置

2.1 引入pom依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.8</version>
</dependency>

2.2 全局配置文件(mybatis-config.xml)

这个配置主要是定义mybatis的工作属性,比如数据库连接信息,如:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 根标签 -->
<configuration>
<properties>
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110?useUnicode=true&amp;characterEncoding=utf-8&amp;allowMultiQueries=true"/>
    <property name="username" value="root"/>
        <property name="password" value="123456"/>
   </properties>

   <!-- 环境,可以配置多个,default:指定采用哪个环境 -->
   <environments default="test">
      <!-- id:唯一标识 -->
      <environment id="test">
         <!-- 事务管理器,JDBC类型的事务管理器 -->
         <transactionManager type="JDBC" />
         <!-- 数据源,池类型的数据源 -->
         <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110" />
            <property name="username" value="root" />
            <property name="password" value="123456" />
         </dataSource>
      </environment>
      <environment id="development">
         <!-- 事务管理器,JDBC类型的事务管理器 -->
         <transactionManager type="JDBC" />
         <!-- 数据源,池类型的数据源 -->
         <dataSource type="POOLED">
            <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
            <property name="url" value="${url}" />
            <property name="username" value="${username}" />
            <property name="password" value="${password}" />
         </dataSource>
      </environment>
   </environments>
  </configuration>

样例模板:

<?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>
   <!-- 环境,可以配置多个,default:指定采用哪个环境 -->
   <environments default="test">
      <!-- id:唯一标识 -->
      <environment id="test">
         <!-- 事务管理器,JDBC类型的事务管理器 -->
         <transactionManager type="JDBC" />
         <!-- 数据源,池类型的数据源 -->
         <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/ssmdemo" />
            <property name="username" value="root" />
            <property name="password" value="123456" />
         </dataSource>
      </environment>
   </environments>
   <mappers>
     <mapper resource="mappers/MyMapper.xml" />  指定mapper配置文件目录
   </mappers>
</configuration>

2.3 配置Map.xml

这个文件用于配置sql操作,也就是针对mapper接口中的方法进行绑定相关的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:命名空间,随便写,一般保证命名空间唯一 -->
<mapper namespace="MyMapper">
   <!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
      resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
    -->
   <select id="selectUser" resultType="com.zpc.mybatis.User">
      select * from tb_user where id = #{id}
   </select>
</mapper>

三、mybatis使用的两种方式

针对mybatis的使用时,有两种方式,一种是传统的方式,另一种是动态代理的方式。后者比较常用。
传统的方式步骤:
1、创建UserDao接口,指定可以执行的sql操作方法
2、实现UserDao接口
3、编写UserDao接口对应的mapper.xml配置文件
4、通过sqlSession对象对应的mapper

动态代理的方式:
对比上面,可以直接省略掉实现UserDao接口这一步,只需要定义接口和编写mapper.xml配置文件即可。

3.1 动态代理方式完整例子

1、创建UserMapper接口(对应原UserDao)

public interface UserMapper {

   /**
    * 登录(直接使用注解指定传入参数名称)
    * @param userName
    * @param password
    * @return
    */
   public User login(@Param("userName") String userName, @Param("password") String password);

   /**
    * 根据表名查询用户信息(直接使用注解指定传入参数名称)
    * @param tableName
    * @return
    */
   public List<User> queryUserByTableName(@Param("tableName") String tableName);

   /**
    * 根据Id查询用户信息
    * @param id
    * @return
    */
   public User queryUserById(Long id);

   /**
    * 查询所有用户信息
    * @return
    */
   public List<User> queryUserAll();

   /**
    * 新增用户信息
    * @param user
    */
   public void insertUser(User user);

   /**
    * 根据id更新用户信息
    * @param user
    */
   public void updateUser(User user);

   /**
    * 根据id删除用户信息
    * @param id
    */
   public void deleteUserById(Long id);
}

2、创建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:命名空间,随便写,一般保证命名空间唯一 ,为了使用接口动态代理,这里必须是接口的全路径名-->
<mapper namespace="com.zpc.mybatis.dao.UserMapper">
    <!--
       1.#{},预编译的方式preparedstatement,使用占位符替换,防止sql注入,一个参数的时候,任意参数名可以接收
       2.${},普通的Statement,字符串直接拼接,不可以防止sql注入,一个参数的时候,必须使用${value}接收参数
     -->
    <select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
        select * from ${tableName}
    </select>

    <select id="login" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where user_name = #{userName} and password = #{password}
    </select>

    <!-- statement,内容:sql语句。
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       resultType:sql语句查询结果集的封装类型,使用动态代理之后和方法的返回类型一致;resultMap:二选一
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where id = #{id}
    </select>
    <select id="queryUserAll" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user
    </select>
    <!-- 新增的Statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
       useGeneratedKeys:开启主键回写
       keyColumn:指定数据库的主键
       keyProperty:主键对应的pojo属性名
     -->
    <insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"
            parameterType="com.zpc.mybatis.pojo.User">
        INSERT INTO tb_user (
        id,
        user_name,
        password,
        name,
        age,
        sex,
        birthday,
        created,
        updated
        )
        VALUES
        (
        null,
        #{userName},
        #{password},
        #{name},
        #{age},
        #{sex},
        #{birthday},
        NOW(),
        NOW()
        );
    </insert>
    <!-- 
       更新的statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
        UPDATE tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">user_name = #{userName},</if>
            <if test="password!=null">password = #{password},</if>
            <if test="name!=null">name = #{name},</if>
            <if test="age!=null">age = #{age},</if>
            <if test="sex!=null">sex = #{sex},</if>
            <if test="birthday!=null">birthday = #{birthday},</if>
            updated = now(),
        </trim>
        WHERE
        (id = #{id});
    </update>
    <!-- 
       删除的statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <delete id="deleteUserById" parameterType="java.lang.String">
        delete from tb_user where id=#{id}
    </delete>
</mapper>

3、全局配置文件mybatis-config.xml引入UserMapper.xml

<mappers>
    <mapper resource="mappers/MyMapper.xml"/>
    <mapper resource="mappers/UserDaoMapper.xml"/>
    <mapper resource="mappers/UserMapper.xml"/>
</mappers>

4、创建UserMapper测试用例

import com.zpc.mybatis.dao.UserMapper;
import com.zpc.mybatis.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class UserMapperTest {

    public UserMapper userMapper;

    @Before
    public void setUp() throws Exception {
        // 指定配置文件
        String resource = "mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 1. 映射文件的命名空间(namespace)必须是mapper接口的全路径
        // 2. 映射文件的statement的id必须和mapper接口的方法名保持一致
        // 3. Statement的resultType必须和mapper接口方法的返回类型一致
        // 4. statement的parameterType必须和mapper接口方法的参数类型一致(不一定)
        this.userMapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    public void testQueryUserByTableName() {
        List<User> userList = this.userMapper.queryUserByTableName("tb_user");
        for (User user : userList) {
            System.out.println(user);
        }
    }

    @Test
    public void testLogin() {
        System.out.println(this.userMapper.login("hj", "123456"));
    }

    @Test
    public void testQueryUserById() {
        System.out.println(this.userMapper.queryUserById("1"));
    }

    @Test
    public void testQueryUserAll() {
        List<User> userList = this.userMapper.queryUserAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }

    @Test
    public void testInsertUser() {
        User user = new User();
        user.setAge(20);
        user.setBirthday(new Date());
        user.setName("大神");
        user.setPassword("123456");
        user.setSex(2);
        user.setUserName("bigGod222");
        this.userMapper.insertUser(user);
        System.out.println(user.getId());
    }

    @Test
    public void testUpdateUser() {
        User user = new User();
        user.setBirthday(new Date());
        user.setName("静静");
        user.setPassword("123456");
        user.setSex(0);
        user.setUserName("Jinjin");
        user.setId("1");
        this.userMapper.updateUser(user);
    }

    @Test
    public void testDeleteUserById() {
        this.userMapper.deleteUserById("1");
    }
}

3.2 mapper接口的编写

这个主要提供sql和接口方法的映射关系,通常写法如下:

public interface UserMapper {
    public void createUser(@Param(“参数ID”) 参数类型 参数名,。。。。。)
}

@Param(“参数ID”) 这个注释是用来给传入的参数指定一个id的,然后可以给mapper.xml中的sql语句通过 #{“参数ID”}进行引用,这样就可以获取到参数的值,每一个参数都得对应一个@Param注释

3.3 动态代理方式注意的问题

1、mapper配置中namespace必须和mapper接口的全路径一致
2、mapper接口的方法名必须和sql定义的id一样,比如select便签中的id
3、mapper接口的方法的输入参数类型和sql定义的parametertype一致
4、mapper接口中方法的输出参数类型必须和sql定义的resultType一致(有时候是resultMap,后面再讲)

四、mapper xml配置详解(重点)

在动态代理的方式下,有两个编写重点,一个是mapper接口的编写,一个是mapper配置文件的编写,mapper xml文件主要就是curd便签的编写,看下例子:

<?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:命名空间,随便写,一般保证命名空间唯一 ,为了使用接口动态代理,这里必须是接口的全路径名-->
<mapper namespace="com.zpc.mybatis.dao.UserMapper">
    <!--
       1.#{},预编译的方式preparedstatement,使用占位符替换,防止sql注入,一个参数的时候,任意参数名可以接收
       2.${},普通的Statement,字符串直接拼接,不可以防止sql注入,一个参数的时候,必须使用${value}接收参数
     -->
    <select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
        select * from ${tableName}
    </select>

    <select id="login" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where user_name = #{userName} and password = #{password}
    </select>

    <!-- statement,内容:sql语句。
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       resultType:sql语句查询结果集的封装类型,使用动态代理之后和方法的返回类型一致;resultMap:二选一
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where id = #{id}
    </select>
    <select id="queryUserAll" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user
    </select>
    <!-- 新增的Statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
       useGeneratedKeys:开启主键回写
       keyColumn:指定数据库的主键
       keyProperty:主键对应的pojo属性名
     -->
    <insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"
            parameterType="com.zpc.mybatis.pojo.User">
        INSERT INTO tb_user (
        id,
        user_name,
        password,
        name,
        age,
        sex,
        birthday,
        created,
        updated
        )
        VALUES
        (
        null,
        #{userName},
        #{password},
        #{name},
        #{age},
        #{sex},
        #{birthday},
        NOW(),
        NOW()
        );
    </insert>
    <!-- 
       更新的statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
        UPDATE tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">user_name = #{userName},</if>
            <if test="password!=null">password = #{password},</if>
            <if test="name!=null">name = #{name},</if>
            <if test="age!=null">age = #{age},</if>
            <if test="sex!=null">sex = #{sex},</if>
            <if test="birthday!=null">birthday = #{birthday},</if>
            updated = now(),
        </trim>
        WHERE
        (id = #{id});
    </update>
    <!-- 
       删除的statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <delete id="deleteUserById" parameterType="java.lang.String">
        delete from tb_user where id=#{id}
    </delete>
</mapper>

4.1 crud便签的使用

4.1.1.select

select – 书写查询sql语句
select中的几个属性说明:
id属性:当前名称空间下的statement的唯一标识。必须。要求id和mapper接口中的方法的名字一致。
resultType:将结果集映射为java的对象类型。必须(和 resultMap 二选一)
parameterType:传入参数类型。可以省略
resultMap:当返回的数据并不是单个实体映射类时,比如多表联合查询,就需要用到这个,后面有详细讲

4.1.2.insert

insert 的几个属性说明:
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
useGeneratedKeys:开启主键回写
keyColumn:指定数据库的主键
keyProperty:主键对应的pojo属性名
标签内部:具体的sql语句。

4.1.3.update

id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。

4.1.4.delete

delete 的几个属性说明:
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。

4.2 #{}和${} 的区别

/**
 * #号
 * @param username1
 * @return
 */
User queryUserListByName1(@Param("username1") String username1);

/**
 * $号
 * @param username2
 * @return
 */
User queryUserListByName2(@Param("username2") String username2);

<select id="queryUserListByName1" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE user_name=#{username1}
</select>

<select id="queryUserListByName2" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE user_name='${username2}'//手动加了引号
</select>

#{} 只是替换?,相当于PreparedStatement使用占位符去替换参数,可以防止sql注入。
${} 是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql;$可以是sql中的任一部分传入到Statement中,不能防止sql注入。

使用${} 去取出参数值信息,需要使用${value}
#{} 只是表示占位,与参数的名字无关,如果只有一个参数,会自动对应。

4.3 动态sql(待补充)

if语句
choose when otherwise
where和set
foreach

4.4 resultMap复杂联合查询重要属性

前面说到select便签中,需要指定resultType和resultMap。如果对单个表进行查询,一般返回的就是一个单个对象,比如User表,查询就是返回User表。如果针对多个表进行联合查询,那么返回的涉及多个表的数据,那么就无法使用单一的一个实体类来映射返回的数据。这时候就无法使用resultType来指定映射的实体类了,需要使用resultMap来指定。下面以案例说明

4.4.1 表关系说明

此案例的业务关系是用户、订单、订单详情、商品信息之间的关系,其中,
一个订单对应一个用户
一个订单可以有多个订单详情
一个订单详情包含一个商品信息

它们的关系时:
订单和用户是一对一关系
订单和订单详情是一对多关系
订单和商品信息是多对多关系

需求说明:
一对一查询:查询订单,并且查询出下单人的信息
一对多查询:查询订单,并且查询出下单人的信息和订单详情
多对多查询:查询订单,并且查询出下单人的信息和订单详情,以及订单详情中的商品信息

4.4.2 一对一查询

先创建order订单表:

sql:
CREATE TABLE tb_order (
id int(11) NOT NULL AUTO_INCREMENT,
user_id int(11) DEFAULT NULL,
order_number varchar(255) DEFAULT NULL,
create datetime DEFAULT NULL,
updated datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

实体类:
public class Order {
    private Integer id;
    private Long userId;
    private String orderNumber;
    private Date created;
    private Date updated;
}

1、方法1:核心思想扩展Order对象,来完成映射
新建OrderUser实体类继承Order:

public class OrderUser extends Order {
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private Integer sex;
    private Date birthday;
    private Date created;
    private Date updated;
}

OrderMapper接口:

public interface OrderMapper {
     OrderUser queryOrderUserByOrderNumber(@Param("number") String number);
}

配置OrderMapper:
这里因为返回的数据可以用继承后的OrderUser来映射,所以可以直接使用resultType来指定类型。

 <mapper namespace="com.zpc.mybatis.dao.OrderMapper">
    <select id="queryOrderUserByOrderNumber" resultType="com.zpc.mybatis.pojo.OrderUser">
      select * from tb_order o left join tb_user u on o.user_id=u.id where o.order_number = #{number}
   </select>
</mapper>

测试:

@Test
public void queryOrderUserByOrderNumber() throws Exception {
    OrderUser orderUser = orderMapper.queryOrderUserByOrderNumber("201807010001");
    System.out.println(orderUser);
}

2、方法2:面向对象的思想,在Order对象中添加User对象。这种方式用的比较多,因为主类和子类耦合度较低,无需通过继承进行关联
在Order对象中添加User属性:

public class Order {
    private Integer id;
    private Long userId;
    private String orderNumber;
    private Date created;
    private Date updated;
    private User user;
}

mapper接口:

/**
 * 根据订单号查询订单用户的信息
 * @param number
 * @return
 */
Order queryOrderWithUserByOrderNumber(@Param("number") String number);

mapper配置:
要注意的是,这里select返回的数据已经无法直接使用单个实体类来映射了,而是涉及到Order和User两个类,那么这时候就需要用到resultType来指定了。先看看例子

<resultMap id="OrderUserResultMap" type="com.zpc.mybatis.pojo.Order" autoMapping="true">
     <id column="id" property="id"/>
     <!--association:完成子对象的映射-->
     <!--property:子对象在父对象中的属性名-->
     <!--javaType:子对象的java类型-->
     <!--autoMapping:完成子对象的自动映射,若开启驼峰,则按驼峰匹配-->
     <association property="user" javaType="com.zpc.mybatis.pojo.User" autoMapping="true">
         <id column="user_id" property="id"/>
     </association>
 </resultMap>

 <select id="queryOrderWithUserByOrderNumber" resultMap="OrderUserResultMap">  这里就指定resultMap为上面定义的resultMap
   select * from tb_order o left join tb_user u on o.user_id=u.id where o.order_number = #{number}
</select>

测试:

@Test
public void queryOrderWithUserByOrderNumber() throws Exception {
    Order order = orderMapper.queryOrderWithUserByOrderNumber("201807010001");
    System.out.println(order.getUser());
}

4.4.3 resultMap用法--主类和子类一对一(association)

<!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性-->
<resultMap id="唯一的标识,后面通过该id引用" type="映射的pojo对象,就是主映射类的全类名" autoMapping="true 是否根据数据库字段名和映射类的属性名称自动映射">
  <id column="数据库表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型,可以省略" property="映射pojo对象的主键属性,就是主映射类中对应的主键的字段名" />

  <!--一般来说,如果映射类中的字段名和数据库的字段名相同,一一对应的话,就会自动进行映射,不需要配置下面的result标签,因为一般会开启autoMapping-->
  <result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/>

  <!--
  association就是定义主对象和子对象的映射关系的
  property :指定子对象在主对象中的字段名,也就是属性名称
  javaType:子对象的类型,需要指定为全类名
   autoMapping:true|false 是否按照数据库字段名和映射类的属性名称自动映射
  --> 
  <association property="pojo的一个对象属性" javaType="pojo关联的pojo对象" >
    <!--
    column:指定与子对象进行关联查询的主对象的数据库表的字段,也就是主数据库表中是用哪个字段字段和子表进行关联查询的。比如说主表order用 user_id,子表User用的是id字段,那么这里指定的就应该为 user_id
    jdbcType:字段类型,一般可以省略,自动映射的,这里是数据库中的类型,比如CHAR
    property:指定子对象的主键属性名

   如果有多个关联字段,那么就有多个对应的id标签。
    -->
    <id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主键属性"/>
    <!--一般来说,如果映射类中的字段名和数据库的字段名相同,一一对应的话,就会自动进行映射,不需要配置下面的result标签,因为一般会开启autoMapping-->
    <result  column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
  </association>
</resultMap>

4.4.4 一对多查询

一对多查询:查询订单,查询出下单人信息并且查询出订单详情。
订单 order类:

public class Order {
    private Integer id;
    private Long userId;
    private String orderNumber;
    private Date created;
    private Date updated;
    private User user;

    private List<OrderDetail> detailList;
}

注意上面的detailList,因为一个订单对应多个订单详情,所以这里用list来保存多个OrderDetail。

订单详情 OrderDetail类:

public class OrderDetail {
    private Integer id;
    private Integer orderId;
    private Double totalPrice;
    private Integer status;
}

mapper接口:

/**
 * 根据订单号查询订单用户的信息及订单详情
 * @param number
 * @return
 */
Order queryOrderWithUserAndDetailByOrderNumber(@Param("number") String number);

mapper配置:

<resultMap id="OrderUserDetailResultMap" type="com.zpc.mybatis.pojo.Order" autoMapping="true">
    <id column="id" property="id"/>
    <!--collection:定义子对象集合映射-->
    <!--association:完成子对象的映射-->
    <!--property:子对象在父对象中的属性名-->
    <!--javaType:子对象的java类型-->
    <!--autoMapping:完成子对象的自动映射,若开启驼峰,则按驼峰匹配-->
    <association property="user" javaType="com.zpc.mybatis.pojo.User" autoMapping="true">
        <id column="user_id" property="id"/>
    </association>

    <!--
    collection便签:用来映射集合类的,主类中的detailList
     -->
    <collection property="detailList" javaType="List" ofType="com.zpc.mybatis.pojo.OrderDetail" autoMapping="true">
        <id column="id" property="id"/>
    </collection>
</resultMap>

 <select id="queryOrderWithUserAndDetailByOrderNumber" resultMap="OrderUserDetailResultMap">
   select * from tb_order o
   left join tb_user u on o.user_id=u.id
   left join tb_orderdetail od on o.id=od.order_id
   where o.order_number = #{number}
</select>

测试:

@Test
public void queryOrderWithUserAndDetailByOrderNumber() throws Exception {
    Order order = orderMapper.queryOrderWithUserAndDetailByOrderNumber("201807010001");
    System.out.println(order.getUser());
    System.out.println(order.getDetailList());
}

4.4.5 resultMap用法--主类和子类一对多(collection)

<!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性-->
<resultMap id="唯一的标识,后面通过该id引用" type="映射的pojo对象,就是主映射类的全类名" autoMapping="true 是否根据数据库字段名和映射类的属性名称自动映射">
  <id column="数据库表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型,可以省略" property="映射pojo对象的主键属性,就是主映射类中对应的主键的字段名" />

  <!--一般来说,如果映射类中的字段名和数据库的字段名相同,一一对应的话,就会自动进行映射,不需要配置下面的result标签,因为一般会开启autoMapping-->
  <result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/>

  <!--
  association就是定义主对象和子对象的映射关系的一对一
  property :指定子对象在主对象中的字段名,也就是属性名称
  javaType:子对象的类型,需要指定为全类名
   autoMapping:true|false 是否按照数据库字段名和映射类的属性名称自动映射
  --> 
  <association property="pojo的一个对象属性" javaType="pojo关联的pojo对象" >
    <!--
    column:指定与子对象进行关联查询的主对象的数据库表的字段,也就是主数据库表中是用哪个字段字段和子表进行关联查询的。比如说主表order用 user_id,子表User用的是id字段,那么这里指定的就应该为 user_id
    jdbcType:字段类型,一般可以省略,自动映射的
    property:指定子对象的主键属性名
    -->
    <id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主键属性"/>
    <!--一般来说,如果映射类中的字段名和数据库的字段名相同,一一对应的话,就会自动进行映射,不需要配置下面的result标签,因为一般会开启autoMapping-->
    <result  column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
  </association>

  <!-- 
  当主类和子类是一对多的关系时,就需要用到collection标签,也就是集合标签
  property:主类中对于子类集合对象的属性名
  ofType:集合对象中子类的全类名
  javaType:集合本身的类型,是list还是其他,一般可以省略
   autoMapping:是否开启字段自动映射
   -->
  <collection property="pojo的集合属性" ofType="集合中的pojo对象" autoMapping="true">

    <!--关联字段
    column:主类对应的数据库表中的关联字段名
    jdbcType:字段数据类型
    property:子类中定义的关联属性名

    如果有多个关联字段,那么就有多个对应的id标签。
    -->
    <id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
    <!--定义除了关联字段之外的字段和子类属性的映射关联,开启automapping且名字一样可省略-->
    <result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" /> 
  </collection>
</resultMap>

多关联字段的情况参考 https://www.cnblogs.com/zhengguangaa/p/9055840.html

4.4.6 多对多查询

多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的每个商品数据。
商品详情和商品信息类:

商品详情
public class OrderDetail {
    private Integer id;
    private Integer orderId;
    private Double totalPrice;
    private Integer status;
    private int itemId;
    private Item item;
}

商品信息
public class Item {
    private Integer id;
    private String itemName;
    private Float itemPrice;
    private String itemDetail;
}

mapper接口:

/**
 * 根据订单号查询订单用户的信息及订单详情及订单详情对应的商品信息
 * @param number
 * @return
 */
Order queryOrderWithUserAndDetailItemByOrderNumber(@Param("number") String number);

mapper配置:

<resultMap id="OrderUserDetailItemResultMap" type="com.zpc.mybatis.pojo.Order" autoMapping="true">
    <id column="id" property="id"/>
    <association property="user" javaType="com.zpc.mybatis.pojo.User" autoMapping="true">
        <id column="user_id" property="id"/>
    </association>
    <collection property="detailList" javaType="List" ofType="com.zpc.mybatis.pojo.OrderDetail" autoMapping="true">

        <id column="detail_id" property="id"/>
        <!--这里就是集合中每个商品详情都对应一个商品信息,用法和上面的一样-->
        <association property="item" javaType="com.zpc.mybatis.pojo.Item" autoMapping="true">
            <id column="item_id" property="id"/>
        </association>
    </collection>
</resultMap>

 <select id="queryOrderWithUserAndDetailItemByOrderNumber" resultMap="OrderUserDetailItemResultMap">
   select * ,od.id as detail_id from tb_order o
   left join tb_user u on o.user_id=u.id
   left join tb_orderdetail od on o.id=od.order_id
   left join tb_item i on od.item_id=i.id
   where o.order_number = #{number}
</select>

测试:

@Test
public void queryOrderWithUserAndDetailItemByOrderNumber() throws Exception {
    Order order = orderMapper.queryOrderWithUserAndDetailItemByOrderNumber("201807010001");
    System.out.println(order);
    System.out.println(order.getUser());
    System.out.println(order.getDetailList());
}

五、springboot整合mybatis

参考 http://blog.didispace.com/spring-boot-learning-21-3-6/

5.1 添加pom依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

这里的驱动按需要引入,这里以mysql为例
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

关于mybatis-spring-boot-starter的版本需要注意:

2.1.x版本适用于:MyBatis 3.5+、Java 8+、Spring Boot 2.1+
2.0.x版本适用于:MyBatis 3.5+、Java 8+、Spring Boot 2.0/2.1
1.3.x版本适用于:MyBatis 3.4+、Java 6+、Spring Boot 1.5

5.2 添加mysql连接信息

在application.properties中添加mysql连接信息

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=xxxxx
spring.datasource.password=xxxxx
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

5.3 mysql中创建目标表

比如:User表,其中包含id(BIGINT)、age(INT)、name(VARCHAR)字段。具体创建命令如下:

CREATE TABLE `User` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

5.4 创建表映射类

比如User类:

@Data
@NoArgsConstructor
public class User {

    private Long id;

    private String name;
    private Integer age;

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

5.5 创建mapper接口

比如UserMapper:

public interface UserMapper {

    User findByName(@Param("name") String name);

    int insert(@Param("name") String name, @Param("age") Integer age);

}

5.6 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.didispace.chapter36.mapper.UserMapper">
    <select id="findByName" resultType="com.didispace.chapter36.entity.User">
        SELECT * FROM USER WHERE NAME = #{name}
    </select>

    <insert id="insert">
        INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})
    </insert>
</mapper>

5.6 将mapper接口所在包添加到扫描位置

在应用主类中增加mapper的扫描包配置:

//使用这个注释即可,指定全包名
@MapperScan("com.didispace.chapter36.mapper")
@SpringBootApplication
public class Chapter36Application {

    public static void main(String[] args) {
        SpringApplication.run(Chapter36Application.class, args);
    }

}

5.7 springboot配置中添加mapper配置文件路径

在application.properties中添加如下:

mybatis.mapper-locations=classpath:mapper/*.xml

classpath表示主类resources相对路径,上面表示resources下的mapper目录下的所有xml文件

5.8 使用mapper接口操作数据库

测试:

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class Chapter36ApplicationTests {

    //直接使用springboot的自动装载配置
    @Autowired
    private UserMapper userMapper;

    @Test
    @Rollback
    public void test() throws Exception {
        userMapper.insert("AAA", 20);
        User u = userMapper.findByName("AAA");
        Assert.assertEquals(20, u.getAge().intValue());
    }

}

猜你喜欢

转载自blog.51cto.com/kinglab/2486097