MyBatis基础

一、MyBatis介绍

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

特性:
1.sql集中管理
之前都是sql语句和java代码耦合在一起,Mybatis解耦 sql语句和java代码,将sql语句分离出来单独管理。
sql语句集中管理的地方称为映射文件,一般将不同功能的sql语句放到不同的映射文件中。
2.动态sql
根据判断条件的不同,执行的不同的sql语句。
3.高级映射
输入映射 和 输出映射。
输入映射:
在这里插入图片描述
输出映射:
在这里插入图片描述

二、入门案例1

映射文件中sql语句唯一性保证:通过映射文件id加上sql语句id保证,其中不同映射文件的sql语句id可以相同。

2.1导包

mybatis
mysql-connector-java
junit

<dependencies>
	<!--使用mybatis,如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中-->
   <dependency>
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis</artifactId>
       <version>3.5.2</version>
   </dependency>
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>5.1.47</version>
       <scope>runtime</scope>
   </dependency>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.13</version>
       <scope>test</scope>
   </dependency>
</dependencies>

2.2mybatis配置文件

mybatis.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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/j23_db?useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--加载的classpath下的映射文件-->
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

2.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">
<!--namespace映射文件的唯一id-->
<mapper namespace="userNamespace">
    <select id="selectUsernameById" resultType="string">
        select username from j23_user_t where id = #{id}
    </select>
</mapper>

2.4通过映射文件的namespace和sql语句id执行sql语句

SqlSessionFactoryBuilder (加载配置文件)→ sqlSessionFactory → sqlSession

package com.cskaoyan;

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.Test;

import java.io.IOException;
import java.io.InputStream;

public class MyTest {

    @Test
    public void mytest() throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //加载classpath下的mybatis的配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory factory = builder.build(inputStream);

        SqlSession sqlSession = factory.openSession();

        //根据id查询username → select username from j23_user_t where id = ?
        //如果查询结果记录数只有一条,使用selectOne
        //selectOne的两个参数:参数1 String 命名空间.sql标签id ,参数2 Object 为sql语句提供参数
        String username = sqlSession.selectOne("userNamespace.selectUsernameById", 1);
        System.out.println("username = " + username);

    }
}

在这里插入图片描述

三、sqlSession的方法

3.1查询 select

3.1.1javabean

根据id查询user:
创建User对象并且在数据库中创建相对应的表。

@Data
public class User {
    Integer id;
    String username;
    String password;
    Integer age;
    String gender;
}

在这里插入图片描述

UserMapper.xml配置sql语句id

<!--查询单条user这个javabean-->
<!--resultType是查询必须的 → 不能省略-->
<!--resultType javabean类型 → javabean的全类名-->
<select id="selectUserById" resultType="com.xxx.bean.User">
    select id,username,password,age,gender from j23_user_t where id = #{id}
</select>

测试类

@Test
    public void mytest2() throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //返回值类型和resultType对应
        User user = sqlSession.selectOne("userNamespace.selectUserById", 1);
        System.out.println("user = " + user);
    }

在这里插入图片描述

补充

通过单元测试的注解简化代码,可以将之前两个单元测试方法中的重复代码整合到一个方法中。
首先,了解junit的几个注解。

import org.junit.*;

public class JunitTest {

    @Before
    public void initEveryTest(){
        System.out.println("hello world");
    }
    @After
    public void afterEveryTest(){
        System.out.println("close");
    }
    @BeforeClass
    public static void initFirst(){
        System.out.println("最初的xxx");
    }
    @AfterClass
    public static void last(){
        System.out.println("最终的xxx");
    }

    @Test
    public void mytest1(){
        //System.out.println("hello world");
        System.out.println("mytest1");
    }
    @Test
    public void mytest2(){
        //System.out.println("hello world");
        System.out.println("mytest2");
    }
    @Test
    public void mytest3(){
        //System.out.println("hello world");
        System.out.println("mytest3");
    }
}

在这里插入图片描述
从测试结果可以看出来@Before、@After注解会在每个单元测试方法执行之前和之后都执行一次,@BeforeClass、@AfterClass只会在所有单元测试方法执行之前和执行之后只执行一次。
利用这几个注解的特性,我们可以将这段重复的代码放到@Before注解的方法中。

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

由于sqlSession不是线程安全的,所以每次都要获得一个sqlSession,所以写到@Before注解的方法中。
而sqlSessionFactory是线程安全的,可以只获取一次,所以写到@BeforeClass注解的方法中。

public class MyModifiedTest {
	//提取出来,作为全局变量使用
    SqlSession sqlSession;
    static SqlSessionFactory factory;
    
    @BeforeClass
    public static void initFactory() throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //加载classpath下的mybatis的配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        factory = builder.build(inputStream);
    }

    @Before
    public void initSqlSession(){
        sqlSession = factory.openSession();
    }

    @Test
    public void mytest(){
       User user = sqlSession.selectOne("userNamespace.selectUserById", 1);
        System.out.println("user = " + user);
    }
}
3.1.2 list
<!--查询list-->
<!--如果是查询多条数据,resultType 依然写的是单条记录对应的类型-->
<select id="selectUsers" resultType="com.xxx.bean.User">
    select id,username,password,age,gender from j23_user_t
</select>
@Test
    public void mytest3(){
        List<User> objects = sqlSession.selectList("userNamespace.selectUsers");
        for (User object : objects) {
            System.out.println(object);
        }
    }

在这里插入图片描述

3.2新增 insert

增删改最后需要提交,所以在测试类中添加一个方法进行提交。

@After
    public void commit(){
        if (sqlSession != null){
            sqlSession.commit();
            sqlSession.close();
        }
    }
<!--新增insert-->
<!--传入的第二个参数object为user 这样的一个javabean对象 → #{} → javabean的成员变量名(get方法)-->
<insert id="insertUser" >
    insert into j23_user_t (username,password,age,gender)
    values
    (#{username},#{password},#{age},#{gender})
</insert>
@Test
public void mytest4(){
    User user = new User();
    user.setUsername("xiaoliang");
    user.setPassword("123456");
    user.setAge(15);
    user.setGender("util man");
    int insert = sqlSession.insert("userNamespace.insertUser", user);
    System.out.println("insert = " + insert );
    //if (sqlSession != null){
    //    sqlSession.commit();
    //    sqlSession.close();
    //}
}

3.3修改 update

<!--修改update-->
<update id="updateUser">
    update j23_user_t set gender = #{gender} where id = #{id}
</update>
@Test
public void mytest5(){
    //根据id更新 gender
    User user = new User();
    user.setId(5);
    user.setGender("util person");
    int update = sqlSession.update("userNamespace.updateUser", user);
    //if (sqlSession != null){
    //    sqlSession.commit();
    //    sqlSession.close();
    //}
}

3.4删除 delete

<!--删除delete-->
<delete id="deleteUser">
    delete from j23_user_t where id = #{id}
</delete>
@Test
public void mytest6(){
    int delete = sqlSession.delete("userNamespace.deleteUser", 5);
}

3.5 小结

1、sqlSession执行不同的方法做增删改查 → 两个参数
String :命名空间+id → 指向唯一的sql语句
→ object:为sql语句提供参数 → #{}
基本类型、包装类、字符串:#{}里是任意写
javabean:#{}写的是javabean的成员变量
2、不同的操作对应的标签不同insert、delete、update、select
3、select标签 → 一定要写resultType属性 不能省略 → resultType里写的是单条记录的类型
4、增删改返回值均为int → 影响数据库的记录数 → 不能够写resultType

四、配置项

4.1properties

4.1.1单项配置

在这里插入图片描述

4.1.2引入文件

在这里插入图片描述
在这里插入图片描述

4.2settings

4.2.1缓存cache

mybatis缓存有两级。

4.2.1.1 一级缓存
默认开启一级缓存,SQLSession级别。
怎样验证一级缓存:使用同一查询,第二次不会进行预编译(Preparing)。
一级缓存在执行sqlSession的commit时失效。
在这里插入图片描述
在这里插入图片描述
4.2.1.2 二级缓存
默认开启,但是我们不能直接使用。需要我们做一下操作:
1、settings里的cacheEnabled为true(默认的)
2、查询结果要序列化
3、映射文件中要开启缓存
在这里插入图片描述
测试二级缓存:
在这里插入图片描述
第一次要进行预编译,sqlSession提交或者关闭后,查询结果被放到二级缓存中,第二次调用查询方法,命中二级缓存,缓存命中率为1/(1+1)=0.5;第三次调用查询方法,命中二级缓存,缓存命中率(1+1)/(1+1+1)=0.6666……;第四次调用查询方法,命中二级缓存,缓存命中率(1+1+1)/(1+1++1)=0.75;第五次调用查询方法,命中二级缓存,缓存命中率(1+1+1+1)/(1+1++1+1)=0.8.

二级缓存是mapper级别的缓存,多个SqlSession可以去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。

4.2.2懒加载

4.2.2.1没有配置的情况下懒加载是否开启
在这里插入图片描述
4.2.2.2开启懒加载后默认为懒加载
在这里插入图片描述
注意测试时,不要输出user或者使用debug模式,因为会调用到toString方法,toString方法中会调用getUserDetail方法,就会对查询UserDetail语句进行预编译,此时不是没有使用懒加载,而是已经调用到相应的方法,自然会执行查询UserDetail语句的预编译。
在这里插入图片描述
4.2.2.3想要立即加载
在这里插入图片描述

4.3typeAliases

type → 别名
resultType javabean的全类名 → 别名

4.3.1对单条配置别名

在这里插入图片描述

4.3.2批量配置别名

在这里插入图片描述
在这里插入图片描述

4.3.3mybatis提供的别名

关注基本类型、包装类、字符串
在这里插入图片描述

4.4typehandlers

输入输出映射过程中参数类型不匹配,使用typehandler类型转换。
举个例子:
User的Javabean:
class User{
Integer id;
UserDetail userDetail; //UserDetail
Integer[] orderIds;
}
User在数据库中的数据表user_t :
id,user_detail(varchar → json) ,order_ids(varchar → json)
tip:需要经常修改查询更新的,要保存成表的形式,而不是像上面的user_detail保存成字符串的格式。

如果直接执行查询语句select id,user_detail as userDetail,order_ids as orderIds from user_t进行封装,数据是封装不进去的。这个时候就可以用到typehandler在中间进行一个数据格式的转换,成功将数据封装到javabean对象中。

typehandler
分析user_detail和userDetail
jdbcType.varchar ↔ javaType.UserDetail
输出映射:jdbcType → javaType(比如查询结果的封装)
输入映射:javaType → jdbcType (比如更新数据)

4.4.1mybatis配置文件中配置typehandler
	<typeHandlers>
        <!--可以写成单个handler的形式:handler要写typehandler的全类名-->
        <!--<typeHandler handler="com.qyt.typehandler.UserDetailTypehandler"/>-->
        <!--也可以写成package包的形式-->
        <package name="com.qyt.typehandler"/>
    </typeHandlers>
4.4.2定义typehandler

在这里插入图片描述

4.4.3输入映射的方法

在这里插入图片描述

4.4.4输出映射的方法

在这里插入图片描述

@MappedTypes(UserDetail.class) //javabean中成员变量类型
@MappedJdbcTypes(JdbcType.VARCHAR) //表中某列的类型
//BaseTypehandler中的泛型写MappedTypes里的类型 → javabean中的成员变量的类型
public class UserDetailTypehandler extends BaseTypeHandler<UserDetail> {
    ObjectMapper objectMapper = new ObjectMapper();
    //输入映射的过程 → 为预编译的sql语句提供参数
    //
    @SneakyThrows
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int index, UserDetail userDetail, JdbcType jdbcType) throws SQLException {
        String value = objectMapper.writeValueAsString(userDetail);
        preparedStatement.setString(index,value);
    }

    //get方法 → 结果集的封装
    @SneakyThrows
    @Override
    public UserDetail getNullableResult(ResultSet resultSet, String columnLabel) throws SQLException {
        System.out.println("string = " + columnLabel);
        String result = resultSet.getString(columnLabel);
        return parseResult(result);
    }

    @SneakyThrows
    @Override
    public UserDetail getNullableResult(ResultSet resultSet, int index) throws SQLException {
        String result = resultSet.getString(index);
        return parseResult(result);
    }

    @SneakyThrows
    @Override
    public UserDetail getNullableResult(CallableStatement callableStatement, int index) throws SQLException {
        String result = callableStatement.getString(index);
        return parseResult(result);
    }

    private UserDetail parseResult(String result) throws JsonProcessingException {
        return objectMapper.readValue(result,UserDetail.class);
    }
}
4.4.5 测试

查询数据:

public interface UserMapper {
    List<User> selectUserById(@Param("id") Integer id);
}
@Test
public void mytest1(){
    List<User> users = userMapper.selectUserById(1);
    System.out.println(users);
}
<mapper namespace="com.qyt.mapper.UserMapper">
    <select id="selectUserById" resultType="com.qyt.bean.User">
      select id,username,password,age,gender,update_date as updateDate,user_detail as userDetail
      from j23_user_t where id = #{id}
    </select>
</mapper>

在这里插入图片描述
更新数据:

public interface UserMapper {
    int updateUserDetailById(@Param("id") Integer id, @Param("userDetail") UserDetail userDetail);
}
	@Test
    public void mytest2(){
        UserDetail userDetail = new UserDetail();
        userDetail.setEmail("1156986445@qyt.com");
        userDetail.setPhone("1594485326");

        int i = userMapper.updateUserDetailById(2, userDetail);
        System.out.println(i);
    }
	<update id="updateUserDetailById">
        update j23_user_t set user_detail = #{userDetail} where id = #{id}
    </update>

在这里插入图片描述
数据类型不匹配经过typehandler进行类型转换后,可以进行封装。

4.5mappers

4.5.1单个文件

相同目录结构
在这里插入图片描述
mybatis.xml

<mappers>
        <!--配置的是接口
                → 接口对应映射文件 → 接口的全类名:namespace
                → 接口中的方法对应 sql语句的id
                → 通过调用接口中的方法 → 唯一与之对应的sql语句
            对应关系:
                1、接口和映射文件要在同一级目录,并且同名
                2、命名空间 为接口的全类名
                3、接口中的方法名 对应映射文件中的sql语句标签的id
            注意事项:同一个映射文件里 标签id不能重复
        -->
        <mapper class="com.cskaoyan.mapper.UserMapper"/>
    </mappers>

UserMapper.xml

<!--namespace与接口UserMapper的全类名相对应-->
<mapper namespace="com.cskaoyan.mapper.UserMapper"> 
	<!--select的id与接口的方法名相对应-->
    <select id="selectUsernameById" resultType="string">
        select username from j23_user_t where id = #{id}
    </select>
</mapper>

UserMapper.java

public interface UserMapper {
    String selectUsernameById(Integer id);
    //接口中不允许重载
    //String selectUsernameById(String username,String password);
}

在这里插入图片描述

4.5.2多个文件

mybatis.xml

    <mappers>
        <!--<mapper class="com.cskaoyan.mapper.UserMapper"/>-->
        <package name="com.cskaoyan.mapper"/>
    </mappers>

4.5.3如何使用接口及其方法(阶段性成果)

@Test
public void mytest1(){
	//mybatis为UserMapper接口提供了一个代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    String username = mapper.selectUsernameById(2);namespace id 参数 结果
    System.out.println("username = " + username);
}

在这里插入图片描述

五、log4j 日志框架

记录,mybatis支持 → 日志输出

5.1引入依赖

在这里插入图片描述

5.2引入配置文件

在这里插入图片描述
log4j.properties

#Appender
#org.apache.log4j.ConsoleAppender(控制台)
#org.apache.log4j.FileAppender(文件)
#org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
#org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
#org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file hibernate.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=d://21mybatis.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

#org.apache.log4j.HTMLLayout(以HTML表格形式布局)
#org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
#org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
#org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
log4j.appender.file2 = org.apache.log4j.FileAppender
log4j.appender.file2.File=d://file2.log
log4j.appender.file2.layout=org.apache.log4j.SimpleLayout
#log4j.appender.file2.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### DEBUG、INFO、WARN、ERROR和FATAL
#日志级别+输出appender
log4j.rootLogger=debug ,file,stdout,file2
5.2.1配置文件

5.2.1.1appender
#配置文件 是 key=value的格式
#Appender #以什么形式进行输出
在这里插入图片描述
5.2.1.2layout
输出内容的格式
log4j配置文件中的一条语句:

log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
  • %d 表示时间,{ABSOLUTE} 是时间格式
  • %p 日志级别,比如DEDUG
    %5p 表示日志级别占用5个字符
  • %c 表示类别信息
    {数字} 类别信息输出多少级 → 从后往前
  • %L 行号:代码中的第多少行输出的
  • %m 表示message消息
  • %n 表示换行

日志级别:
debug、info、warn、error、fatal
级别越高,内容越少 → 字数越少,事情越大
级别越低,内容越多 → 信息量越大
低级别会输出当前级别以及更高级别的内容
在这里插入图片描述
自行输出日志:

import org.apache.log4j.Logger;
import org.junit.Test;

public class LogTest {
    Logger logger = Logger.getLogger(LogTest.class);

    @Test
    public void mytest(){
        logger.debug("debug");
        logger.info("info");
        logger.warn("warn");
        logger.error("error");
        logger.fatal("fatal");
    }
}

在这里插入图片描述

六、高级映射

6.1输入映射

为sql语句提供参数 → #{}里面填什么
接口中的方法传入什么样的参数相对应的mapper配置文件中的#{}里面写什么。

6.1.1单个参数

6.1.1.1基本类型、包装类、字符串
#{}里任意写
在这里插入图片描述
6.1.1.2javabean或map
#{}里写javabean的成员变量名或map的key
→ 实际上使用的javabean的get方法,或map的get
在这里插入图片描述

6.1.2多个参数

#{}中写什么
param1,param2…paramn
arg0,arg1…arg(n-1)

UserMapper接口中的方法

//多个参数
//                        param1           param2           param3
//                        arg0             arg1             arg2
int selectIdByUser(String username, String password,Integer age);
// username 通过字符串传入
// password 通过map传入
// age 通过user传入
int selectIdByUserMulti(String username,Map map,User user);

UserMapper.xml

<!--多个参数-->
<!--<select id="selectIdByUser" resultType="int">
    select id from j23_user_t where username = #{param1} and password = #{param2} and age = #{param3}
</select>-->
<select id="selectIdByUser" resultType="int">
    select id from j23_user_t where username = #{arg0} and password = #{arg1} and age = #{arg2}
</select>

<select id="selectIdByUserMulti" resultType="int">
    select id from j23_user_t where username = #{param1} and password = #{param2.password} and age = #{param3.age}
</select>

测试方法

/**
* 多个参数
*/
@Test
public void mytest3(){
   int id = mapper.selectIdByUser("laotan", "456", 22);
   System.out.println(id);

   String username = "laotan";
   Map map = new HashMap();
   map.put("password", "456");
   User user = new User();
   user.setAge(22);

   int id2 = mapper.selectIdByUserMulti(username, map, user);
   System.out.println(id2);
}

日志控制台输出

14:56:51,411 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUser:137 - ==>  Preparing: select id from j23_user_t where username = ? and password = ? and age = ?
14:56:51,480 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUser:137 - ==> Parameters: laotan(String), 456(String), 22(Integer)
14:56:51,533 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUser:137 - <==      Total: 1
2
14:56:51,535 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUserMulti:137 - ==>  Preparing: select id from j23_user_t where username = ? and password = ? and age = ?
14:56:51,536 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUserMulti:137 - ==> Parameters: laotan(String), 456(String), 22(Integer)
14:56:51,538 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUserMulti:137 - <==      Total: 1
6.1.3使用注解@Param

接口中方法的形参上 → 写了什么,就用什么
@Param注解的value属性中写了什么值 → #{}中就用什么值
以多个参数为例:
UserAnnotationMapper接口方法

//多个参数
    int selectIdByUser(@Param("username") String username, @Param("password") String password, @Param("age") Integer age);
    // username 通过字符串传入
    // password 通过map传入
    // age 通过user传入
    int selectIdByUserMulti(@Param("username") String username, @Param("map") Map map, @Param("user") User user);

UserAnnotationMapper.xml

<!--多个参数-->
    <select id="selectIdByUser" resultType="int">
        select id from j23_user_t where username = #{username} and password = #{password} and age = #{age}
    </select>

    <select id="selectIdByUserMulti" resultType="int">
        select id from j23_user_t where username = #{username} and password = #{map.password} and age = #{user.age}
    </select>

测试方法

@Test
    public void mytest3(){
        int id = mapper.selectIdByUser("laotan", "456", 22);
        System.out.println(id);

        String username = "laotan";
        Map map = new HashMap();
        map.put("password", "456");
        User user = new User();
        user.setAge(22);

        int id2 = mapper.selectIdByUserMulti(username, map, user);
        System.out.println(id2);
    }

日志控制台输出

15:04:08,964 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUser:137 - ==>  Preparing: select id from j23_user_t where username = ? and password = ? and age = ?
15:04:09,028 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUser:137 - ==> Parameters: laotan(String), 456(String), 22(Integer)
15:04:09,053 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUser:137 - <==      Total: 1
2
15:04:09,055 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUserMulti:137 - ==>  Preparing: select id from j23_user_t where username = ? and password = ? and age = ?
15:04:09,056 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUserMulti:137 - ==> Parameters: laotan(String), 456(String), 22(Integer)
15:04:09,060 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUserMulti:137 - <==      Total: 1

tip:如果遇到报错中有syntax,说明sql语句有问题,或者格式有问题。

6.2输出映射

sql语句查询结果集的封装 → mapper接口中的方法的返回值来定义结果集。

6.2.1基本类型、包装类、字符串

select和from之间,通常只写一个列或其别名。

单条记录:
根据id查询某个用户名:

接口UserMapper

	//通常是查询满足某个条件的  某一列的值或记录数
	String selectUsernameById(@Param("id") Integer id);

UserMapper.xml配置文件

	<select id="selectUsernameById" resultType="java.lang.String">
	    select username from j23_user_t where id = #{id}
	</select>

测试方法

	@Test
    public void mytest1(){
        String username = mapper.selectUsernameById(2);
        logger.info("username = " + username);
    }

在这里插入图片描述
多条记录:
查询全部用户名:

接口UserMapper

	//返回值封装为数组或list之间可以互相转换 → 映射文件里的写法是相同的,返回值写数组或list都行
	String[] selectUsernameArray();
	//List<String> selectUsernameArray();
	List<String> selectUsernameList();

UserMapper.xml配置文件

	<select id="selectUsernameArray" resultType="string">
        select username from j23_user_t
    </select>
    <select id="selectUsernameList" resultType="java.lang.String">
        select username from j23_user_t
    </select>

测试方法

	@Test
    public void mytest2(){
        String[] users1 = mapper.selectUsernameArray();
        logger.info(Arrays.asList(users1));
        List<String> users2 = mapper.selectUsernameList();
        logger.info(users2);
    }

在这里插入图片描述
单条和多条比较:
接口中的方法的返回值是不同的。
执行的sql语句查询结果允许的记录数不同。

6.2.2javabean

封装数据需满足条件:查询结果的列名和javabean的成员变量名一致。
当javabean成员变量名和数据库中的表的列名不一致时,可以通过as起别名的形式修改查询结果的列名。
表的列名不一定是查询结果的列名,输出映射看的是查询结果的列名。
查询结果的列名和javabean的成员变量名对应 → set方法。

单条记录:
接口UserMapper

	//javabean
	User selectUserById(@Param("id") Integer id);

UserMapper.xml配置文件

	<!--javabean-->
    <select id="selectUserById" resultType="com.cskaoyan.bean.User">
        select id,username,password,age,gender,update_date as updateDate from j23_user_t where id = #{id}
    </select>  

测试方法

    @Test
    public void mytest3(){
        User user = mapper.selectUserById(2);
        logger.info("user = " + user);
    }

在这里插入图片描述
多条记录:

接口UserMapper

	User[] selectUserArray();
	List<User> selectUserList();

UserMapper.xml配置文件

	<!--user数组-->
    <select id="selectUserArray" resultType="com.cskaoyan.bean.User">
        select id,username,password,age,gender,update_date as updateDate from j23_user_t
    </select>
    <!--userList-->
    <select id="selectUserList" resultType="com.cskaoyan.bean.User">
        select id,username,password,age,gender,update_date as updateDate from j23_user_t
    </select>

测试方法

    @Test
    public void mytest4(){
        User[] users1 = mapper.selectUserArray();
        for (User user : users1) {
            logger.info("users1:" + user);
        }
        List<User> users2 = mapper.selectUserList();
        for (User user : users2) {
            logger.info("users2:" + user);
        }
    }

单条和多条比较:
接口中的方法的返回值是不同的。
执行的sql语句查询结果允许的记录数不同。

6.2.3resultMap

resultType:查询结果的列名和javabean成员变量名 一致
resultMap:查询结果的列名和javabean成员变量名 对应

之前写的resultType的地方可以替换为resultMap,查询一定要写resultType或resultMap。

column:查询结果的列名
property:javabean的成员变量名(set)

接口UserMapper

	 //使用resultMap column ↔ property
	User selectUserByIdMap(@Param("id") Integer id);
	User[] selectUserArrayMap();
	List<User> selectUserListMap();

UserMapper.xml配置文件

	<!--resultMap-->
    <!--resultMap标签的type属性写的就是原先resultType里写的内容 → javabean的全类名、typeAlias-->
    <resultMap id="userMap" type="com.cskaoyan.bean.User">
        <!--column始终是查询结果的列名-->
        <!--property始终是父标签类型javabean的成员变量名-->
        <result column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="age" property="age"/>
        <result column="gender" property="gender"/>
        <result column="update_date" property="updateDate"/>
    </resultMap>
    <!--javabean-->
    <select id="selectUserByIdMap" resultMap="userMap">
        select id,username,password,age,gender,update_date from j23_user_t where id = #{id}
    </select>

    <!--user数组-->
    <select id="selectUserArrayMap" resultMap="userMap">
        select id,username,password,age,gender,update_date from j23_user_t
    </select>
    <!--userList-->
    <select id="selectUserListMap" resultMap="userMap">
        select id,username,password,age,gender,update_date from j23_user_t
    </select>

测试方法

   @Test
    public void mytest5(){
        User[] users1 = mapper.selectUserArrayMap();
        for (User user : users1) {
            logger.info("users1:" + user);
        }
        List<User> users2 = mapper.selectUserListMap();
        for (User user : users2) {
            logger.info("users2:" + user);
        }
    }

七、动态sql

通过在映射文件中写标签实现动态sql。

7.1 where标签

将where语句后的内容放入到where标签中

接口UserMapper

  User selectUserById(@Param("id") Integer id);

UserMapper.xml

	<select id="selectUserById" resultMap="userMap">
        select id,username,password,age,gender,update_date from j23_user_t
        <where>
            id = #{id}
        </where>
    </select>

测试方法

	@Test
    public void mytest1(){
        User user = mapper.selectUserById(2);
        logger.info("user = " + user);
    }

在这里插入图片描述

7.2 if标签

接口UserMapper

List<User> selectUserByUsernameAndPassword(@Param("username") String username,
                                               @Param("password") String password);

UserMapper.xml
test属性中写if的判断条件,决定是否拼接if标签中的sql语句,test属性中可以使用的值是输入映射的值,即@Param中的value属性值。

	<!--利用 if标签来做 条件查询-->
    <!--if标签中的test属性写的是条件 → 输入映射可以写的值 →  @Param注解的value属性值-->
    <select id="selectUserByUsernameAndPassword" resultMap="userMap">
        select id,username,password,age,gender,update_date from j23_user_t
        <where>
            <if test="username != null">
                username = #{username}
            </if>
            <if test="password != null">
                and password = #{password}
            </if>
        </where>
    </select>

测试方法
where标签自动将多余的连接词去除,而且如果where标签里没有内容,则不会拼接where字段。

	@Test
    public void mytest2(){
        //username和password都不为null
        //Preparing → select id,username,password,age,gender,update_date from j23_user_t where username = ? and password = ?
        List<User> users1 = mapper.selectUserByUsernameAndPassword("laotan", "456");

        //username不为null,password为null
        //Preparing → select id,username,password,age,gender,update_date from j23_user_t where username = ?
        List<User> users2 = mapper.selectUserByUsernameAndPassword("laotan", null);

        //username为null,password不为null
        //未使用where标签前,预编译的sql语句出错,多了一个and
        //Preparing → select id,username,password,age,gender,update_date from j23_user_t  where and password = ?
        //使用where标签后 → select id,username,password,age,gender,update_date from j23_user_t WHERE password = ?
        List<User> users3 = mapper.selectUserByUsernameAndPassword(null, "456");

        //username和password都为null
        //未使用where标签前,预编译的sql语句出错,多了一个where
        //Preparing → select id,username,password,age,gender,update_date from j23_user_t where
        //使用where标签后 → select id,username,password,age,gender,update_date from j23_user_t
        List<User> users4 = mapper.selectUserByUsernameAndPassword(null, null);
    }
7.2.1 test中的语法

变量:输入映射可以使用的值 → @Param注解的value属性值

常量:

  • 数值:直接写 1 2
  • 字符串:单引号 ‘qyt’
  • 布尔类型:test = “isMarried”,isMarried值是true或者false

符号:

  • 等于: ==
  • 不等于: !=
  • 大于: gt
  • 小于: lt
  • 大于等于:大于或等于 ,比如 age gt 20 or age == 20
  • 小于等于:小于或等于 ,比如 age lt 20 or age == 20

连接词:多个条件之间的关系

  • and 且
  • or 或

null值: null
字符串长度:‘字符串’.length


案例:如果年龄≥ 20,查询条件 ≥该年龄
如果年龄< 20,查询条件<该年龄
接口UserMapper

	//根据年龄做查询
    //如果年龄≥ 20 查询条件 ≥该年龄
    //如果年龄< 20 查询条件 <该年龄
    List<User> selectUserByAge(@Param("age") Integer age);

UserMapper.xml

	<select id="selectUserByAge" resultMap="userMap">
        select id,username,password,age,gender,update_date from j23_user_t
        <where>
            <!--if标签test属性中中的 > , < 的用法-->
          <if test="age gt 20 or age == 20">
              age &gt;= #{age} <!--xml文件中的 > , < 的用法-->
          </if>
          <if test="age lt 20">
              age &lt; #{age}
          </if>
        </where>
    </select>

测试方法

	@Test
    public void mytest3(){
        List<User> users1 = mapper.selectUserByAge(22);
        List<User> users2 = mapper.selectUserByAge(18);
        List<User> users3 = mapper.selectUserByAge(23);
    }

在这里插入图片描述

7.3 choose-when-otherwise组合标签

if标签没有else对应,但有choose-when-otherwise组合标签,相当于if-else。

关于使用,when和otherwise作为choose子标签,when标签中仍然可以使用test属性代表if,otherwise代表的就是else,不写test属性。

接口UserMapper

//使用choose-when-otherwise来实现通过年龄查询用户信息
    List<User> selectUserByAgeChooseWhen(@Param("age") Integer age);

UserMapper.xml

	<select id="selectUserByAgeChooseWhen" resultMap="userMap">
        select id,username,password,age,gender,update_date from j23_user_t
        <where>
            <choose>
                <when test="age gt 20 or age == 20">
                    age &gt;= #{age}
                </when>
                <otherwise> <!--注意otherwise标签里面就不要写test属性了,会报错-->
                    age &lt; #{age}
                </otherwise>
            </choose>
        </where>
    </select>

测试方法

	@Test
    public void mytest4(){
        List<User> users1 = mapper.selectUserByAgeChooseWhen(22);
        List<User> users2 = mapper.selectUserByAgeChooseWhen(18);
        List<User> users3 = mapper.selectUserByAgeChooseWhen(23);
    }

在这里插入图片描述

7.4 trim标签

属性:
prefixOverrides:如果最前面出现了什么内容,去除掉
suffixOverrides:如果最后面出现了什么内容,去除掉
prefix:最前面拼接一个内容
suffix:最后面拼接一个内容

接口UserMapper

	//根据id选择性更新,传入的user对象,哪个成员变量值不为null,就更新该值
    int updateSelectUser(@Param("user") User user);

UserMapper.xml

	<!--使用trim标签去掉多余的逗号-->
    <update id="updateSelectUser">
        update j23_user_t
        <!--prefix在前面拼接set,suffixOverrides去除多余的逗号-->
        <trim prefix="set" suffixOverrides=",">
            <if test="user.username != null">
                username = #{user.username},
            </if>
            <if test="user.password != null">
                password = #{user.password},
            </if>
            <if test="user.age != null and user.age != 0">
                age = #{user.age},
            </if>
            <if test="user.gender != null">
                gender = #{user.gender}
            </if>
        </trim>
        <where>
            id = #{user.id}
        </where>
    </update>

测试方法

	@Test
    public void mytest5(){
        User user = new User();
        user.setAge(20);
        //user.setGender("female");
        user.setId(2);
        int update = mapper.updateSelectUser(user);
        logger.info("update = " + update);
    }

在这里插入图片描述

7.5 set标签

使用set标签对上面的实现进行一定改进。

接口UserMapper

int updateSelectUserSet(@Param("user") User user);

UserMapper.xml

	<!--set标签 :会自动拼接set同时去除逗号-->
    <update id="updateSelectUserSet">
        update j23_user_t
        <set>
            <if test="user.username != null">
                username = #{user.username},
            </if>
            <if test="user.password != null">
                password = #{user.password},
            </if>
            <if test="user.age != null and user.age != 0">
                age = #{user.age},
            </if>
            <if test="user.gender != null">
                gender = #{user.gender}
            </if>
        </set>
        <where>
            id = #{user.id}
        </where>
    </update>

测试方法

 	@Test
    public void mytest6(){
        User user = new User();
        user.setAge(20);
        //user.setGender("female");
        user.setId(2);
        int update = mapper.updateSelectUserSet(user);
        logger.info("update = " + update);
    }

在这里插入图片描述

7.6 sql-include标签

sql标签定义一个sql片段,include标签引用sql标签里的内容。

作用:将一部分重复的sql语句提取出去,来进行引用。

	<!--用法1:sql片段-->
    <sql id="baseSelectUser">
        select id,username,password,age,gender,update_date from j23_user_t
    </sql>
    
    <!--用法2:sql片段里面可以包括其他标签-->
    <sql id="where_id">
        <where>
            id = #{id}
        </where>
    </sql>
    
	<!--引用sql片段-->
    <select id="selectUserById" resultMap="userMap">
        <include refid="baseSelectUser"/>
        <include refid="where_id"/>
    </select>

	<!--用法3:把表的列放到sql标签中-->
    <sql id="base_column_list">
        id,username,password,age,gender,update_date
    </sql>
    <!--引用sql片段作为sql语句中表的列-->
	<select id="selectUserByUsernameAndPassword" resultMap="userMap">
        select <include refid="base_column_list"/> from j23_user_t
        <where>
            <if test="username != null">
                username = #{username}
            </if>
            <if test="password != null">
                and password = #{password}
            </if>
        </where>
    </select>

7.7 selectKey标签

insert时使用

7.7.1 order为AFTER

举个例子:
有个user对象插入到数据库,id是自增,插入到数据库后获得id。
接口UserMapper

	//selectKey
    int insertUser(@Param("user") User user);

UserMapper.xml

<!--selectKey → 查询结果封装给输入映射传入的值-->
    <!--
        order:selectKey中的语句执行顺序 → 相对于insert语句的
        keyColumn:selectKey查询语句中查询结果的列名
        keyProperty:查询结果要封装给谁 → 通常写的是和输入映射的值相关 → @Param注解的value属性值相关
        resultType:keyProperty的类型
    -->
    <insert id="insertUser">
        <selectKey order="AFTER" keyColumn="increasedId" keyProperty="user.id" resultType="int">
            select LAST_INSERT_ID() as increasedId
        </selectKey>
        insert into j23_user_t
        (username,password,age,gender,update_date)
        values(#{user.username},#{user.password},#{user.age},#{user.gender},#{user.updateDate})
    </insert>

测试方法

	@Test
    public void mytest7(){
        User user = new User();
        user.setUsername("zzz");
        user.setPassword("789");
        user.setAge(20);
        user.setGender("female");
        user.setUpdateDate(new Date());
        int insert = mapper.insertUser(user);
        logger.info("insert = " + insert);
        logger.info("user = " + user);
    }

在这里插入图片描述

7.7.2 order为BEFORE

场景:插入一个user数据,username是随机生成的UUID → uuid生成在执行insert之前
接口UserMapper

int insertUserOrderBefore(@Param("user") User user);

UserMapper.xml

	<insert id="insertUserOrderBefore">
		<!--获取作为username的uuid是在insert操作之前获取的-->
        <selectKey order="BEFORE" keyColumn="username" keyProperty="user.username" resultType="string">
            select uuid() as username
        </selectKey>
        insert into j23_user_t
        (username,password,age,gender,update_date)
        values (#{user.username},#{user.password},#{user.age},#{user.gender},#{user.updateDate})
    </insert>

测试方法

	@Test
    public void mytest8(){
        User user = new User();
        //user.setUsername("zzz");
        user.setPassword("789");
        user.setAge(20);
        user.setGender("female");
        user.setUpdateDate(new Date());
        int insert = mapper.insertUserOrderBefore(user);
        logger.info("insert = " + insert);
        logger.info("user = " + user);
    }

在这里插入图片描述

7.8 insert标签中的属性

通过insert标签中的useGeneratedKeys属性获取自增主键。
接口UserMapper

	//使用insert标签的属性 获得自增的主键
    int insertUserGenerateKey(@Param("user") User user);

UserMapper.xml

 	<!--自增主键-->
    <insert id="insertUserGenerateKey" useGeneratedKeys="true" keyProperty="user.id">
        insert into j23_user_t
        (username,password,age,gender,update_date)
        values (#{user.username},#{user.password},#{user.age},#{user.gender},#{user.updateDate})
    </insert>

测试方法

	@Test
    public void mytest9(){
        User user = new User();
        user.setUsername("ali");
        user.setPassword("789");
        user.setAge(20);
        user.setGender("female");
        user.setUpdateDate(new Date());
        int insert = mapper.insertUserGenerateKey(user);
        logger.info("insert = " + insert);
        logger.info("user = " + user);
    }

在这里插入图片描述

7.9 foreach标签

和foreach语句做一个类比,帮助理解。
在这里插入图片描述
接口UserMapper

	//foreach
    List<User> selectUserByIdInArray(Integer[] ids); //collection = array
    List<User> selectUserByIdInList(List<Integer> ids); //collection = list
    List<User> selectUserByIdInArrayParam(@Param("ids") Integer[] ids); //collection = ids
    List<User> selectUserByIdInListParam(@Param("ids") List<Integer> ids); // collection = ids

UserMapper.xml

	<!--foreach-->
    <sql id="base_select">
        select id,username,password,age,gender,update_date from j23_user_t
    </sql>
    <!--collection:填的值和输入映射相关
            数组:array
            list:list
            @Param:value属性值
        separator:foreach标签里的内容每两个之间的分隔符
        open和close:整个foreach执行完成最前和最后的补充内容
    -->
    <select id="selectUserByIdInArray" resultMap="userMap">
        <include refid="base_select"/>
        <where>
            id in
            <foreach collection="array" item="id" separator="," open="(" close=")">
                #{id}
            </foreach>
        </where>
    </select>
    <select id="selectUserByIdInList" resultMap="userMap">
        <include refid="base_select"/>
        <where>
            id in
            <foreach collection="list" item="id" separator="," open="(" close=")">
                #{id}
            </foreach>
        </where>
    </select>
    <select id="selectUserByIdInArrayParam" resultMap="userMap">
        <include refid="base_select"/>
        <where>
            id in
            <foreach collection="ids" item="id" separator="," open="(" close=")">
                #{id}
            </foreach>
        </where>
    </select>
    <select id="selectUserByIdInListParam" resultMap="userMap">
        <include refid="base_select"/>
        <where>
            id in
            <foreach collection="ids" item="id" separator="," open="(" close=")">
                #{id}
            </foreach>
        </where>
    </select>

测试方法

	//foreach
    @Test
    public void mytest11(){
        Integer[] ids = {1, 2, 3, 4, 5};
        List<User> users = mapper.selectUserByIdInArray(ids);
        for (User user : users) {
            logger.info(user);
        }
    }

在这里插入图片描述

7.9.1insert多条记录

接口UserMapper

	//foreach → 批量插入多条记录
    int insertUsers(@Param("users") List<User> users);

UserMapper.xml

	<insert id="insertUsers" useGeneratedKeys="true" keyProperty="users.id">
        insert into j23_user_t (username,password,age,gender,update_date)
        values
        <foreach collection="users" item="user" separator=",">
            (#{user.username},#{user.password},#{user.age},#{user.gender},#{user.updateDate})
        </foreach>
    </insert>

测试方法

 @Test
    public void mytest12(){
        User user1 = new User(null, "user1", "pwd1", 21, "male",new Date());
        User user2 = new User(null, "user2", "pwd2", 22, "male",new Date());
        User user3 = new User(null, "user3", "pwd3", 23, "male",new Date());
        User user4 = new User(null, "user4", "pwd4", 24, "male",new Date());
        ArrayList<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);
        users.add(user3);
        users.add(user4);

        int i = mapper.insertUsers(users);
        System.out.println("insert = " + i);

    }

在这里插入图片描述

八、多表查询

8.1 一对一

以一个用户(User)对应一张用户详情表(UserDetail)为例查询。

8.1.1数据库表关系

在这里插入图片描述

8.1.2 javabean关系

在这里插入图片描述

8.1.3 查询

8.1.3.1 分次查询
接口UserMapper

	//根据username查询user信息 → user需要绑定对应的UserDetail信息
    User selectUserByName(@Param("username") String username);

UserMapper.xml

	<resultMap id="userMap" type="com.cskaoyan.bean.User">
        <result property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="age" column="age"/>
        <result property="gender" column="gender"/>
        <result property="updateDate" column="update_date"/>
        <!--
            column: 查询结果的列名 → 并且为第二次查询提供参数
            property: 始终对应父标签type的javabean的成员变量名
            select: 第二次查询的  命名空间+id
        -->
        <association property="userDetail" column="id"
                     select="com.cskaoyan.mapper.UserMapper.selectUserDetailByUid"/>
    </resultMap>
    <select id="selectUserByName" resultMap="userMap">
        select id,username,password,age,gender,update_date from j23_user_t where username = #{username}
    </select>
    <!--第二次查询#{}里的值如何写 → 随便写-->
    <select id="selectUserDetailByUid" resultType="com.cskaoyan.bean.UserDetail">
        select id,phone,email from j23_user_detail_t where user_id = #{userId}
    </select>

测试方法

	@Test
    public void mytest1(){
        User user = mapper.selectUserByName("laotan");
        System.out.println(user);
    }

在这里插入图片描述
8.1.3.2 连接查询
通常使用的是左连接查询 能够保证左表的信息的完整进行封装 ,右表中的字段允许为null。
连接查询是一次性将信息都查询出来,然后再进行封装。

接口UserMapper

User selectUserByNameLeft(@Param("username") String username);

UserMapper.xml

	<!--
        	不管使用的是哪里的
            property: 始终对应的是其父标签类型的javabean成员变量名
            column: 始终对应的查询结果的列名
    -->
    <resultMap id="userMapLeft" type="com.cskaoyan.bean.User">
        <result property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="age" column="age"/>
        <result property="gender" column="gender"/>
        <result property="updateDate" column="update_date"/>
        <association property="userDetail" javaType="com.cskaoyan.bean.UserDetail">
            <result property="id" column="udid"/>
            <result property="phone" column="phone"/>
            <result property="email" column="email"/>
        </association>
    </resultMap>
    <!--连接查询过程中查询结果单额列名出现重复 → 通常使用as起个别名-->
    <select id="selectUserByNameLeft" resultMap="userMapLeft">
        select
        u.id,u.username,u.password,u.age,u.gender,u.update_date,
        ud.id as udid,ud.phone,ud.email
        from j23_user_t u
        LEFT JOIN j23_user_detail_t ud on u.id = ud.user_id
        where u.username = #{username}
    </select>

测试方法

	@Test
    public void mytest2(){
        User user = mapper.selectUserByNameLeft("laotan");
        logger.info(user);
    }

在这里插入图片描述

8.2 一对多

以一个用户(User)可以对应多个订单信息(Order)为例根据用户名查询用户信息和相对应的用户订单信息。

8.2.1数据库表关系

在这里插入图片描述

8.2.2javabean关系

在这里插入图片描述

8.2.3查询

8.2.3.1 分次查询
接口UserMapper

User selectUserByNameOrder(@Param("username") String username);

UserMapper.xml

    <resultMap id="userMap" type="com.qyt.bean.User">
        <result property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="age" column="age"/>
        <result property="gender" column="gender"/>
        <result property="updateDate" column="update_date"/>
        <collection property="orders" column="id"
                    select="com.qyt.mapper.UserMapper.selectOrderByUid"/>
    </resultMap>
    <select id="selectUserByNameOrder" resultMap="userMap">
      select id,username,password,age,gender,update_date from j23_user_t where username = #{username};
    </select>
    <resultMap id="orderMap" type="com.qyt.bean.Order">
        <result property="id" column="id"/>
        <result property="ordername" column="ordername"/>
        <result property="money" column="money"/>
    </resultMap>
    <select id="selectOrderByUid" resultMap="orderMap">
        SELECT id,ordername,money from j23_order_t where user_id =#{user_id};
    </select>

测试方法

	@Test
    public void mytest1() {
        User user = mapper.selectUserByNameOrder("laotan");
        logger.info(user);
    }

在这里插入图片描述
8.2.3.2连接查询
接口UserMapper

User selectUserByNameOrderLeft(@Param("username") String username);

UserMapper.xml

	<resultMap id="userMapLeft" type="com.qyt.bean.User">
        <result property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="age" column="age"/>
        <result property="gender" column="gender"/>
        <result property="updateDate" column="update_date"/>
        <collection property="orders" ofType="com.qyt.bean.Order">
            <result property="id" column="oid"/>
            <result property="ordername" column="ordername"/>
            <result property="money" column="money"/>
        </collection>
    </resultMap>
    <select id="selectUserByNameOrderLeft" resultMap="userMapLeft">
        SELECT u.id,u.username,u.password,u.age,u.gender,u.update_date,
              o.id as oid,o.ordername,o.money
        from j23_user_t u
        LEFT JOIN j23_order_t o on u.id = o.user_id
        where u.username = #{username}
    </select>

测试方法

	@Test
    public void mytest2() {
        User user = mapper.selectUserByNameOrderLeft("laotan");
        logger.info(user);
    }

在这里插入图片描述

8.3 多对多

以学生和课程的多对多关系为例,一个学生可以选修多门课程,一个课程可以有多个学生学习。场景:根据学生姓名查询学生信息及学生的选课信息,如果username(用户名)为null,则查询所有学生信息。

8.3.1数据库表关系

在这里插入图片描述

8.3.2javabean关系

在这里插入图片描述

8.3.3查询

8.3.3.1 分次查询
接口UserMapper

List<Student> selectStudentByName(@Param("studentName") String studentName);

UserMapper.xml

	<!--返回值封装成map-->
    <resultMap id="studentMap" type="com.qyt.bean.Student">
        <result property="id" column="id"/>
        <result property="studentName" column="student_name"/>
        <collection property="courses" column="id"
                    select="com.qyt.mapper.StudentMapper.selectCourseByStudentId"/>
    </resultMap>
    <!--第一次查询-->
    <select id="selectStudentByName" resultMap="studentMap">
        select id,student_name from j23_student_t
        <!--引用sql片段-->
        <include refid="selective_student_name"/>
    </select>

    <!--第二次查询,根据student_id对课程信息表和中间关系表进行连接查询,查询学生的所有课程信息-->
    <select id="selectCourseByStudentId" resultType="com.qyt.bean.Course">
        select c.id,c.course_name as courseName
        from j23_course_t c
        LEFT JOIN j23_relationship_t r on c.id = r.course_id
        where student_id = #{stuid}
    </select>

测试方法

	@Test
    public void test1(){
        List<Student> students = studentMapper.selectStudentByName("laotan");
        logger.info(students);
    }

在这里插入图片描述

	@Test
    public void test2(){
        List<Student> students = studentMapper.selectStudentByName(null);
        logger.info(students);
    }

在这里插入图片描述
8.3.3.2 连接查询
接口UserMapper

List<Student> selectStudentByNameLeft(@Param("studentName") String studentName);

UserMapper.xml

	<sql id="selective_student_name">
        <where>
            <if test="studentName != null">
                student_name = #{studentName}
            </if>
        </where>
    </sql>
	<resultMap id="studentMapLeft" type="com.qyt.bean.Student">
        <result property="id" column="id"/>
        <result property="studentName" column="student_name"/>
        <collection property="courses" ofType="com.qyt.bean.Course">
            <result property="id" column="cid"/>
            <result property="courseName" column="course_name"/>
        </collection>
    </resultMap>
    <select id="selectStudentByNameLeft" resultMap="studentMapLeft">
        SELECT
        s.id,
        s.student_name,
        c.id as cid,
        c.course_name
        from j23_student_t s
        LEFT JOIN j23_relationship_t r on s.id = r.student_id
        LEFT JOIN j23_course_t c on c.id = r.course_id
        <include refid="selective_student_name"/>
    </select>

测试方法

	@Test
    public void test3(){
        List<Student> students = studentMapper.selectStudentByNameLeft("laotan");
        logger.info(students);
    }

在这里插入图片描述

    @Test
    public void test4(){
        List<Student> students = studentMapper.selectStudentByNameLeft(null);
        logger.info(students);
    }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值