62-Mybatis高级介绍
Mybatis高级介绍
Mybatis 高级查询:
ResutlMap 属性 :
建立对象关系映射 :
/*
resultType
如果实体的属性名与表中字段名一致,将查询结果自动封装到实体类中 ResutlMap
如果实体的属性名与表中字段名不一致,可以使用ResutlMap 实现手动封装到实体类中 因为不指定的话,是根据对应变量名称和get,set方法操作的,但是表的名称总会有可能会改变
那么可能对应的变量,set,get都匹配不了,所以,我们需要一种可以指向的操作
让他们按照我的指向进行封装,而不是默认的指向
*/
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lagou.mapper.UserMapper"><!--根据id查询用户--><select id="findUserById" parameterType="int" resultType="user">select * from user where id = #{id}</select><!-- resultMap:手动配置实体属性与表中字段的映射关系,完成手动封装-指向对应的唯一标识的映射,读取到的一系列数据会被这个标识进行确定位置,从而完成映射从这里可以得出,读取映射文件,不只是读取一次,然后操作一次,也可以全部读取,将数据封装好,进行操作这里就是如此,封装的对应MappedStatement对象(即类),每个元素基本就是一个MappedStatement对象--><select id="findAllResultMap" resultMap="userResultMap">select * from User</select><!-- id唯一标识,可以随便写,type也受别名影响,与resultType不同的是,resultType是用来默认封装而type是自定义封装,会先判断默认封装,如果默认封装找不到了,则会看自定义封装
当数据库改变时,我们可以将resultType改为type,这样就可以自定义封装了
由于数据库的字段,最终都会是大小写忽略的过来判断,所以下面column是可以进行大小写的替换的,但我们通常都是小写
就如数据库也默认小写一样,所以像这样没有判断的基本大小写忽略--><resultMap id="userResultMap" type="user">
<!--手动配置映射关系--><!--id:用来配置主键--><id property="id" column="id"/><!--result:用来配置普通字段--><result property="username" column="username"/><!--property可以是变量的小写,但不能是对应的大写,column可以大小写忽略
因为对应字段基本上是小写字段--><result property="birthday" column="birthday"/><result property="sex" column="sex"/><result property="address" column="address"></result>
<!-- 大小写通常是忽略的,有些情况下,如判断下,基本是对应的小写,如#{},和property都是判断变量是否符合--></resultMap></mapper>
package com.lagou.test;import com.lagou.domain.User;
import com.lagou.mapper.UserMapper;
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;
import java.util.List;/****/
public class MybatisText {/*Mybatis的dao层mapper代理方式测试*/@Testpublic void test1() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();//这里就不是使用方法来调用了,而是直接使用mapper代理对象//代理对象,可以看成,通过接口方法,即会传入对应的Class类,来进行解析//一般的我们是直接调用对应读取方法,传入自己写的参数,即进行sql的获得信息//这里我们通过解析对应接口方法,来获得对应的sql信息//方法类路径+方法名称=sql路径传参//parameterType=接口方法参数//而方法返回值,是当作这个方法的返回值的,返回的是resultType的类型,所以也要一致才行UserMapper mapper = sqlSession.getMapper(UserMapper.class);//上面说明了对应参数,那么调用这个方法,会在其中,通过反射,进行参数传递//使得类似于实现类的操作(实际上就是帮我们创建一个类似实现类的对象)//这样就形成了我们只用接口来进行实现类的操作了,User userById = mapper.findUserById(6);System.out.println(userById);sqlSession.close();}@Testpublic void test2() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> allResultMap = mapper.findAllResultMap();for (User user : allResultMap) {System.out.println(user);}sqlSession.close();}
}
多条件查询(三种) :
使用 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数
使用注解,引入 @Param() 注解获取参数
使用pojo 对象传递参数
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lagou.mapper.UserMapper"><!--根据id查询用户--><select id="findUserById" parameterType="int" resultType="user">select * from user where id = #{id}</select><!-- resultMap:手动配置实体属性与表中字段的映射关系,完成手动封装-指向对应的唯一标识的映射,读取到的一系列数据会被这个标识进行确定位置,从而完成映射从这里可以得出,读取映射文件,不只是读取一次,然后操作一次,也可以全部读取,将数据封装好,进行操作这里就是如此,封装的对应MappedStatement对象(即类),每个元素基本就是一个MappedStatement对象--><select id="findAllResultMap" resultMap="userResultMap">select * from User</select><!-- id唯一标识,可以随便写,type也受别名影响,与resultType不同的是,resultType是用来默认封装而type是自定义封装,会先判断默认封装,如果默认封装找不到了,则会看自定义封装
当数据库改变时,我们可以将resultType改为type,这样就可以自定义封装了
由于数据库的字段,最终都会是大小写忽略的过来判断,所以下面column是可以进行大小写的替换的,但我们通常都是小写
就如数据库也默认小写一样,所以像这样没有判断的基本大小写忽略--><resultMap id="userResultMap" type="user">
<!--手动配置映射关系--><!--id:用来配置主键--><id property="id" column="id"/><!--result:用来配置普通字段--><result property="username" column="username"/><!--property可以是变量的小写,但不能是对应的大写,column可以大小写忽略
因为对应字段基本上是小写字段--><result property="birthday" column="birthday"/><result property="sex" column="sex"/><result property="address" column="address"></result>
<!-- 大小写通常是忽略的,有些情况下,如判断下,基本是对应的小写,如#{},和property都是判断变量是否符号--></resultMap><!--多条件查询 1--><select id="findByIdAndUsername1" resultMap="userResultMap"><!--select * from user where id = #{arg0} and username = #{arg1}-->select * from user where id = #{param1} and username = #{param2}
<!--一般情况下,若需要两个以及两个以上的参数(不是类),parameterType只能操作其中一个,基本上是左边,且多余的一般默认为null(除非是相同的开始参数,如都是param1,那么就是对应的值)
因为没有替换(需要在数据库里加上null,而不是null字符串,使用程序得到的),这时若在
数据库表里没有找到对应值
那么一般不会报错,与?不同,没有替换,那么就会当作就是这个#{username},所以报错的,而?,则必须替换,否则报错
即找到了,那么一般就会报错,因为例外一个没有进行替换所以为了实现这个操作,那么就基本不能使用parameterType
我们可以使用使用 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数
arg0代表传递参数的第一位参数,param1也是
一般传统的需要一些特殊操作,才可实现多参数的传递,而这里的代理,可以直接进行多参数的传递(实际上也是特殊的操
作)
特殊操作,可以理解为使用map集合和类
使得对应替换为集合替换,对于类,当然使用的是替换类
而单参数,当然替换的是对应参数(名字可以随便起,因为最后只会替换单参数,写的名称会直接覆盖,不会像类和map那样
识别)
但必须有,否则报错,因为没有替换的值(实际上会识别#{}是否有对应名称,没有就会报错,对所有方式都会进行,所以随
便写的就是为了防止这个错误)
可以理解为他们赋值给Object
而多参数,可以理解为一个map集合(基本所有元素都可以使用),所以上面的特殊操作,也可以看成是map集合的操作
我们替换的就是对应集合的数,即key的值,且没有大小写忽略
因为map集合的key基本不能是不一样的获得的
最后:parameterType,一般是可以不写的,即会自动识别对应类型,写了是为了好知道传入的参数是什么,基本只是一个识
别
--></select><!--多条件查询 2--><select id="findByIdAndUsername2" resultMap="userResultMap">select * from user where id = #{id} and username = #{username}<!--
假设,有两个@Param("id"),那么一般后面的覆盖前面的,假如两个
只有一个有@Param("id"),那么有id时操作该值
否则就相当于没有操作,即他是一个别名--></select><!--多条件查询 3注意:id不能相同,否则报错--><select id="findByIdAndUsername3" resultMap="userResultMap" parameterType="user">select * from user where id = #{id} and username = #{Username}</select></mapper>
package com.lagou.mapper;import com.lagou.domain.User;
import org.apache.ibatis.annotations.Param;import java.util.List;/****/
public interface UserMapper {/*根据id查询用户*/public User findUserById(int id);/*查询所以用户*/public List<User> findAllResultMap();/*多条件查询方式一*/public List<User> findByIdAndUsername1(int id,String username);/*多条件查询方式二
这个@Param注解,将操作的参数进行名称设置(对应的#{}或者${}名称必须一致,即替换名称必须一致,否则报错)
无论是否是单个值(单个值名称随便改变的那个,也需要一致)
而不是用默认的arg和param进行map键的添加
虽然也可一起使用,在不是别名时,即@Param注解对应的别名时,则操作对应的默认arg和param
单个不可,因为@Param设置后,不可操作单个了
所以当传递这个参数时,会根据是否有该注解,进行判断,然后进行key添加,即这个标识是可以随便起的
之所以可以这样,是因为反射是可以反射出注解的*/public List<User> findByIdAndUsername2(@Param("id")int id, @Param("username")String username);/*多条件查询方式三*/public List<User> findByIdAndUsername3(User user);}
package com.lagou.test;import com.lagou.domain.User;
import com.lagou.mapper.UserMapper;
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;
import java.util.List;/****/
public class MybatisText {/*Mybatis的dao层mapper代理方式测试*/@Testpublic void test1() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();//这里就不是使用方法来调用了,而是直接使用mapper代理对象//代理对象,可以看成,通过接口方法,即会传入对应的Class类,来进行解析//一般的我们是直接调用对应读取方法,传入自己写的参数,即进行sql的获得信息//这里我们通过解析对应接口方法,来获得对应的sql信息//方法类路径+方法名称=sql路径传参//parameterType=接口方法参数//而方法返回值,是当作这个方法的返回值的,返回的是resultType的类型,所以也要一致才行UserMapper mapper = sqlSession.getMapper(UserMapper.class);//上面说明了对应参数,那么调用这个方法,会在其中,通过反射,进行参数传递//使得类似于实现类的操作(实际上就是帮我们创建一个类似实现类的对象)//这样就形成了我们只用接口来进行实现类的操作了,User userById = mapper.findUserById(6);System.out.println(userById);sqlSession.close();}@Testpublic void test2() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> allResultMap = mapper.findAllResultMap();for (User user : allResultMap) {System.out.println(user);}sqlSession.close();}/*多条件查询:方式一*/@Testpublic void test3() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> allResultMap = mapper.findByIdAndUsername1(6, "自动提交事务");for (User user : allResultMap) {System.out.println(user);}sqlSession.close();}/*多条件查询:方式二*/@Testpublic void test4() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> allResultMap = mapper.findByIdAndUsername2(6, "自动提交事务");for (User user : allResultMap) {System.out.println(user);}sqlSession.close();}/*多条件查询:方式三*/@Testpublic void test5() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user1 = new User();user1.setId(6);user1.setUsername("自动提交事务");List<User> allResultMap = mapper.findByIdAndUsername3(user1);for (User user : allResultMap) {System.out.println(user);}sqlSession.close();}
}
模糊查询 :
方式一:
/*模糊查询:方式一*/@Testpublic void test6() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> mapper1 = mapper.findByUsername("%自动%");//替换,实际上会帮我们自动添加单引号,之所以不自动添加双引号,是因为整体由双引号包括的//这也是sql注入的来源之一,而占位的会将sql注入的操作进行转义for (User user : mapper1) {System.out.println(user);}sqlSession.close();}
<!--模糊查询:方式一 string不能写成String,没有封装String这个别名--><select id="findByUsername" resultMap="userResultMap" parameterType="string">select * from user where username like #{username}</select>
/*
模糊查询:方式一*/public List<User> findByUsername(String username);
方式二 :
<!--模糊查询:方式二--><select id="findByUsername2" resultMap="userResultMap" parameterType="string"><!--parameterType是基本数据类型或者String时,${}里面的值只能写value注意:#{}是占位符,即当找到这个时,会自动的添加单引号
替换后,sql语句最终在平台执行,且只能是单独的,不能被引号包括,否则报错${}是sql原样拼接(无论是否单独都替换,不会出现替换报错,与#{}不一样,被引号包括时,会报错,即${}范围大)
即当找到这个时,不会加上单引号,由于不会加上单引号
所以拼接时基本不会进行引号转义,即替换时没有引号转义,所以会出现sql注入-->select * from user where username like '${value}'
<!--通过实验,value其实也可以进行随便写,可能特殊时候,只能写value吧
但要注意的是:它里面不能是对应的数字,如111,否则是不会进行替换的,即默认为null
所以一般返回没有数据(因为like不能直接的查出null)
--></select>
/*
模糊查询:方式二
*/@Testpublic void test7() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> mapper1 = mapper.findByUsername2("%自动%");for (User user : mapper1) {System.out.println(user);}sqlSession.close();}
/*
模糊查询:方式二*/public List<User> findByUsername2(String username);
#{}:表示一个占位符号
通过 #{} 可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,# {}可以有效防止sql 注入
#{} 可以接收简单类型值或 pojo 属性值
如果 parameterType 传输单个简单类型值, #{} 括号中可以是value 或其它名称
${}:表示拼接sql 串
通过 ${} 可以将 parameterType 传入的内容拼接在 sql 中且不进行jdbc 类型转换,会出现 sql 注入问题
${} 可以接收简单类型值或 pojo 属性值
如果 parameterType 传输单个简单类型值, ${} 括号中基本只能是value(实际上也是可以随便写的,但数字好像不可以替换)
当然了,进行替换的,基本不能空的,否则会检查报错
Mybatis 映射文件深入:
我们很多时候有这种需求,向数据库插入一条记录后
希望能立即拿到这条记录在数据库中的主键值(比如自增时,没有传递对应主键,即不知道对应主键是对少,但要获得对应主键)
实际上由于只是获得对应语句,所以也是有可能出现查询,实现删除语句的
useGeneratedKeys :
<!--添加用户:获得返回主键:方式一useGeneratedKeys="true" 声明返回主键,帮你找到自增当时的主键,并赋值给对应id,只作用与自增和insert元素其他元素可以发现,没有这个属性,且注意:若表不是自增的,使用这个属性,会检查报错若是自增的,他会利用自增量来计算最终主键的,因为数据库表的自增是可以根据自增当前量(起始值)来计算获得keyProperty="id" 把返回主键的值,封装到实体的id 属性中,可以是对应变量的小写注意:只适用于主键自增的数据库,mysql 和 sqlserver 支持,oracle 不支持-->
<insert id="saveUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})
</insert>
/*
添加用户:返回主键方式一
*/@Testpublic void test8() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setUsername("自动提交事务");user.setBirthday(new Date());user.setSex("男");user.setAddress("北京");System.out.println(user);mapper.saveUser(user);//通过自增起始值计算主键,从而获得对应主键System.out.println(user);sqlSession.commit();sqlSession.close();}
/*
添加用户:获得返回主键方式一*/public void saveUser(User user);/*
注意:只适用于主键自增的数据库,mysql 和sqlserver 支持,oracle 不行
selectKey :
在这之前,我要介绍一个sql函数
select last_insert_id();
-- last_insert_id()函数可以获得最后一次添加的内容的主键,但是自己添加的不算,也就是直接添加的不算
-- 只能是因为自动添加的最后一个,所以也通常操作自增
-- 自增也使得该字段必须是主键,所以整体来说是获得最后一次添加内容的主键
<!--添加用户:获得返回主键:方式二--><insert id="saveUser2" parameterType="user" >
<!--
selectKey 适用范围广,支持所有类型数据库,实际上就是再次执行语句,多用于获得主键
当然也可以执行其他语句,如计算平均值等等,将对应值进行赋值
其中KeyColumn是可以省略的,实际上只是看执行语句的结果,因为会自动识别,写上他,可以知道这里是干什么的
且基本只能操作单个数,即类和集合等等,基本操作不了keyColumn="id" 指定主键列名keyProperty="id" 指定主键封装到实体的 id 属性中resultType="int" 指定赋值元素类型,必须正确,否则报错,因为只有确定类型,才可进行赋值,否则不知道是数字还是
字符串而使得整型出现字母了,order="AFTER" 设置在 sql 语句执行后,执行此语句order="BEFORE" 设置在 sql 语句执行前,执行此语句
--><selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="int">
<!--这个方式,会覆盖掉useGeneratedKeys方式,即不会执行useGeneratedKeys方式了,因为判断的条件,只能是一个
即是selectKey,找到这个,不执行useGeneratedKeys方式,否则执行useGeneratedKeys方式
-->select last_insert_id();
</selectKey>
<!--selectKey只能在insert元素使用,可以发现,其他元素里没有这个标签
我们也知道,要获得最后一次插入的主键,自然的这个方法需要后调用,所以设置order="AFTER"表示后调用
即下面sql语句调用完后,执行这个语句
这时oracle也就可以使用了,将AFTER改成BEFORE
-->insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})</insert>
/*
添加用户:返回主键方式二
*/@Testpublic void test9() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setUsername("热热热");user.setBirthday(new Date());user.setSex("男");user.setAddress("北京");System.out.println(user);mapper.saveUser2(user);//也是赋值给对应变量System.out.println(user);sqlSession.commit();sqlSession.close();}
/*
添加用户:获得返回主键方式二*/public void saveUser2(User user);
动态 SQL :
当我们要根据不同的条件,来执行不同的 sql 语句的时候,需要用到动态sql
在这之前,我们知道,getParameter()方法,在没有指定参数,返回null
有指定参数,没写值的为"“(空值),一般不会出现没有参数,那么通常都是”"(空值)
那么先思考,如何通过好的办法,进行上面三种组合的sql语句
一般的,我们可能会这样想
String sql = "select * from user where " --where后面加个空格,防止连接上
if(name != ""){
sql += "name=" + name
}
if(price != ""){
sql += "and price=" + price
}
if(color != ""){
sql += "and color=" + color
}
-- 像上面的操作,但是上面有一个缺点,当name为""时,其他拼接就会有问题因为where后面不能接and
-- 我们可以这样写,在where后面加上'1'='1'或者true
-- 使得可以加上and不影响,这样操作true和false的结果,任然在后面sql上
-- 那么就可以使得全部都可以由and了
-- 即
String sql = "select * from user where true" --true后面加个空格,防止连接上
if(name != ""){
sql += "and name=" + name
}
if(price != ""){
sql += "and price=" + price
}
if(color != ""){
sql += "and color=" + color
}
-- 这样就可以实现拼接了
但是我们现在在Mybatis里,如何在配置文件里进行这样的操作呢
动态 SQL 之< if >:
根据id 和username 查询,但是不确定两个都有值
/*动态sql的if标签:多条件查询*/public List<User> findByIdAndUsernameIf(User user);
<!--动态sql的if标签:多条件查询--><select id="findByIdAndUsernameIf" resultType="user" parameterType="user">select * from user<!--test里面就是表达式where标签:相当于where 1=1,即where true,但是如果没有条件的话,不会拼接上where关键字实际上就是当出现一个条件成立,那么就会添加上相当于where true的语句否则不会添加,那么就是select * from user了,这样就没有多余的代码了--><where><if test="id != null">and id = #{id}</if><if test="username != null">and username = #{username}</if></where><!--读取的配置文件会将这些if的对应属性值和内容进行替换拼接这里说明一下:对应替换,是使用get方法,若没有get方法则直接赋值且无论是get后面名称还是直接赋值,首字母大小写忽略其中表达式里的对应同样变量也会替换,如id,username替换之前,会将对应信息变成字符串
所以无论对应类型是什么,都是字符串之间的值比较这里我们使用null来判断的,实际上前端传递的大多数都是""但是这里可以看到引号之间不好变化(单引号和双引号只能分开,使得单引号和双引号有一个不能判断)所以一般的我们都是null,实际上后台传递的基本是null(""会进行处理,一般自己处理,与null一起通过test来处理)
因为字符串默认null(int是Integer的)若直接的int,可能后台会进行处理但基本上都是类--></select>
/*
动态sql的if标签:多条件查询
*/@Testpublic void test10() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setId(49);user.setUsername("热热热");List<User> byIdAndUsernameIf = mapper.findByIdAndUsernameIf(user);for (User user1 : byIdAndUsernameIf) {System.out.println(user1);}sqlSession.close();}
当然也有一个这个方式
<!-- choose 标签相当于swtich 语句 when 标签相当于case 语句 ,即只会添加一个,因为只会执行一个when,即那个符合先执行,从上到下
即有一个成立,那么choose结束,且是从上到下的执行,若都不成立,则执行otherwise,最后结束chooseotherwise 标签相当于default 语句
-->
<select id="findByIdAndUsernameChoose" parameterType="user" resultType="user"> SELECT * FROM `user` <where> <choose> <when test="id != null"> AND id = #{id} </when> <when test="username != null"> AND username = #{username} </when> <otherwise> AND 1=1 </otherwise> </choose> </where>
</select>
这个方式不怎么用,因为基本只能添加一个sql语句,所以了解就可
动态 SQL 之< set >:
动态更新user 表数据,如果该属性有值就更新,没有值不做处理
就如在回显时,删除对应值,使得对应值为空,这时进行修改了,所以我们可以防止这种情况
/*动态sql的foreach标签:多值查询:数组*/public List<User> findByArray(Integer[] ids);
/*动态sql之set: 动态更新*/@Testpublic void test11() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();// 当前返回的 其实是基于UserMapper所产生的代理对象:底层:JDK动态代理 实际类型:proxyUserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setId(3);user.setUsername("子慕最帅");user.setAddress("北京海淀区拉勾网");mapper.updateIf(user);sqlSession.commit();sqlSession.close();}
<!--动态sql之set : 动态更新--><update id="updateIf" parameterType="user">update user<!--<set> : 在更新的时候,会自动添加set关键字,还会去掉最后一个条件的逗号set标签其他元素基本上都可以使用,实际上无论是什么标签,都是读取存放对应数据,使用字符串代码执行,从而
获得对应sql语句最后去掉逗号
虽然可以直接使用if,但是主要的,是逗号的去除,所以我们也是使用set的--><set><if test="username != null">username = #{username},</if><if test="birthday != null">birthday = #{birthday},</if><if test="sex != null">sex = #{sex},</if><if test="address != null">address = #{address},</if></set><!--可以看出,读取的数据其实也是按照顺序来的即会通过结束标签进行全部读取位置,读取完,拼接后,才会进行后续读取连接-->where id = #{id}</update>
动态 SQL 之< foreach >
foreach 主要是用来做数据的循环遍历
例如: select * from user where id in (1,2,3) 在这样的语句中,传入的参数部分基本是依靠foreach 遍历才能实现
因为上面说的,基本都是判断,不是循环
集合方式:
/*动态sql的foreach标签:多值查询:数组*/public List<User> findByArray(Integer[] ids);
/*动态sql之foreach: 多值查询*/@Testpublic void test12() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();// 当前返回的 其实是基于UserMapper所产生的代理对象:底层:JDK动态代理 实际类型:proxyUserMapper mapper = sqlSession.getMapper(UserMapper.class);List<Integer> ids = new ArrayList<>();ids.add(11);ids.add(6);ids.add(2);List<User> users = mapper.findByList(ids);//虽然多参数基本是map集合操作,但这只是对于替换来说,因为需要key//而不用替换的,基本就可以使用list集合来进行直接数据封装//因为最终的,就是将数据进行拼接而已,具体什么方式,由Mybatis决定for (User user : users) {System.out.println(user);}sqlSession.close();}
<!--动态sql的foreach标签:多值查询:根据多个id值查询用户--><select id="findByList" parameterType="list" resultType="user">
<!--list在Mybatis中也有别名-->select * from user<where><!--collection : 代表要遍历的集合元素,通常写collection或者list 其他也可以,具体看百度,如array则是使用数组而之所以可以写collection,是因为多态但是若有多个参数,那么需要指定了,即他这里相当于获取参数的方式而已(相当于#{},或者${}),否则会报错的open : 代表语句的开始部分close : 代表语句的结束部分item : 代表遍历结合中的每个元素,生成的变量名,使用的#{}或者${}替换必须与这个名称一样,否则报错separator: 分隔符--><foreach collection="collection" open="id in (" close=")" item="id" separator=",">#{id}</foreach></where></select>
数组方式:
/*动态sql的foreach标签:多值查询:数组*/public List<User> findByArray(Integer[] ids);
/*动态sql之foreach: 多值查询 :数组
*/@Testpublic void test13() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();// 当前返回的 其实是基于UserMapper所产生的代理对象:底层:JDK动态代理 实际类型:proxyUserMapper mapper = sqlSession.getMapper(UserMapper.class);Integer[] ids = {2,6,11};List<User> byArray = mapper.findByArray(ids);for (User user : byArray) {System.out.println(user);}sqlSession.close();}
<!--动态sql的foreach标签:多值查询:根据多个id值查询用户--><select id="findByArray" parameterType="int" resultType="user">
<!--由这里看出,parameterType与替换联系,虽然不写没事-->
select * from user<where><!--collection : 代表要遍历的集合元素,通常写collection或者list 其他也可以,具体看百度如array使用数组但是若有多个参数,那么需要指定了,即他这里相当于获取参数的方式而已(相当于#{},或者${}),否则会报错的open : 代表语句的开始部分close : 代表语句的结束部分item : 代表遍历结合中的每个元素,生成的变量名,使用的#{}或者${}替换必须与这个名称一样,否则报错separator: 分隔符--><foreach collection="array" open="id in (" close=")" item="id" separator=",">#{id} </foreach></where></select>
SQL 片段 :
映射文件中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
-- 如
select * from user
-- 可以看到很多sql里基本都是这个,若我们将user表改成users表,那么基本需要改变很多地方
-- 所以我们需要一个提取,只要改变一处,他们就都可以改变
<!--这个标签可以封装好一个语句,由于所以元素基本会封装到一个MappedStatement对象
所以可以知道,他们都是一步一步读取,经过一系列操作,将结果语句放到对应对象
当读取到sql元素时,那么就会将对应对象中的对应标签值进行替换,从而形成了改变
-->
<sql id="selectUser">select * from user</sql>
<!--引入方式,即改变方式,将对应的 select * from user改成下面的操作,将sql元素写在外面,使得读取到
即他们的位置是sql元素与其他元素平级,include与sql语句元素平级,否则的话,就会报错
-->
<include refid="selectUser"></include>
知识小结 :
/*
<select>:查询 <insert>:插入 <update>:修改 <delete>:删除 <selectKey>:返回主键 <where>:where 条件 <if>:if 判断 <foreach>:for 循环 <set>:set 设置 <sql>:sql 片段抽取 */
Mybatis 核心配置文件深入:
plugins 标签 :
MyBatis 可以使用第三方的插件来对功能进行扩展,分页助手PageHelper 是将分页的复杂操作进行封装
使用简单的方式即可获得分页的相关数据
开发步骤:
导入通用PageHelper 的坐标 :
<!-- 分页助手 -->
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5</version>
</dependency>
<dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>0.9.1</version>
</dependency>
在 mybatis 核心配置文件中配置PageHelper 插件:
<!-- 顺序问题,需要放在environments元素前面--><plugins><!--通过反射,创建对应类,以及对应数据库类型的方式--><plugin interceptor="com.github.pagehelper.PageHelper"><property name="dialect" value="mysql"/><!--dialect指定方言,因为不同数据库的对应分页操作不同,如mysql是limit--></plugin></plugins>
/*核心配置文件深入:plugin标签:pageHelper*/@Testpublic void test15() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();// 当前返回的 其实是基于UserMapper所产生的代理对象:底层:JDK动态代理 实际类型:proxyUserMapper mapper = sqlSession.getMapper(UserMapper.class);// 设置分页参数// 参数1: 当前页// 参数2: 每页显示的条数PageHelper.startPage(1,2);//进行参数的传递,实际上操作了其他对象,即分页相关List<User> users = mapper.findAllResultMap();//通过反射,找到插件对应的类,执行对应类的方法,使得操作了其他对象(Page)//最后调用对应语句时,会操作那个其他对象//然后添加对应的limit的sql//如果不指定对应类型,那么就会报错for (User user : users) {System.out.println(user);}// 获取分页相关的其他参数PageInfo<User> pageInfo = new PageInfo<User>(users);//实际上也执行了对应的语句,只不过返回的是分页的(这时加上了对应limit的sql)//就是说在原来语句上进行操作返回,所以说,插件的那个配置地方,基本在原来sql后进行操作的System.out.println("总条数"+ pageInfo.getTotal());System.out.println("总页数"+ pageInfo.getPages());System.out.println("是否是第一页"+ pageInfo.isIsFirstPage());
/*
//其他分页的数据
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示长度:"+pageInfo.getPageSize());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage()); */sqlSession.close();}
知识小结 :
MyBatis 核心配置文件常用标签:
properties 标签:该标签可以加载外部的 properties 文件
typeAliases 标签:设置类型别名
environments 标签:数据源环境配置标签
plugins 标签:配置MyBatis 的插件
Mybatis 多表查询 :
数据库表关系介绍:
关系型数据库表关系分为:
/*
一对一 一对多 多对多
*/
举例:
/*
人和身份证号就是一对一
一个人只能有一个身份证号
一个身份证号只能属于一个人 用户和订单就是一对多,订单和用户就是多对一
一个用户可以下多个订单
多个订单属于同一个用户 学生和课程就是多对多
一个学生可以选修多门课程
一个课程可以被多个学生选修 特例
从数据层面看,一个订单实际上也只是对应一个用户,对于表来说就是多对一
所以在Mybatis中,一个订单只从属于一个用户,将多对一看成了一对一 了
但无论是如何多表,我们的约束只是一个规范而已,所以约束也是可以不写的,但主键还是最好要
*/
数据库表:
注意:表有约束,直接复制,可能会创建不了,最好一个一个的复制执行
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `ordertime` varchar(255) DEFAULT NULL, `total` DOUBLE DEFAULT NULL, `uid` INT(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `uid` (`uid`), CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `orders` VALUES ('1', '2020-12-12', '3000', '1');
INSERT INTO `orders` VALUES ('2', '2020-12-12', '4000', '1');
INSERT INTO `orders` VALUES ('3', '2020-12-12', '5000', '2'); -- ----------------------------
-- Table structure for sys_role
-- ---------------------------- DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `rolename` VARCHAR(255) DEFAULT NULL, `roleDesc` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'CTO', 'CTO');
INSERT INTO `sys_role` VALUES ('2', 'CEO', 'CEO'); -- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` ( `userid` INT(11) NOT NULL, `roleid` INT(11) NOT NULL, PRIMARY KEY (`userid`,`roleid`), KEY `roleid` (`roleid`), CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `user` (`id`), CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `sys_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1');
INSERT INTO `sys_user_role` VALUES ('2', '1');
INSERT INTO `sys_user_role` VALUES ('1', '2');
INSERT INTO `sys_user_role` VALUES ('2', '2');
package com.lagou.domain;public class Orders {private Integer id;private String ordertime;private Double total;private Integer uid;// 表示当前订单属于那个用户 association//不要认为是多对一,就不用创建对于变量//因为无论怎么创建,都是为了更好的方便操作而已,所以可以将这个订单当成是对应用户的//即一般情况下,有自己对应的一和对应的多,都可以创建对应的信息,一的一般是类,多的一般是集合private User user;@Overridepublic String toString() {return "Orders{" +"id=" + id +", ordertime='" + ordertime + '\'' +", total=" + total +", uid=" + uid +", user=" + user +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getOrdertime() {return ordertime;}public void setOrdertime(String ordertime) {this.ordertime = ordertime;}public Double getTotal() {return total;}public void setTotal(Double total) {this.total = total;}public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}
}
package com.lagou.domain;import java.io.Serializable;
import java.util.Date;
import java.util.List;public class User implements Serializable {private Integer id;private String username;private Date birthday;private String sex;private String address;// 表示多方关系:集合 : 代表了当前用户所具有的订单列表 collectionprivate List<Orders> ordersList;// 表示多方关系:集合 : 代表了当前用户所具有的角色列表 collectionprivate List<Role> roleList;@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", birthday=" + birthday +", sex='" + sex + '\'' +", address='" + address + '\'' +", ordersList=" + ordersList +", roleList=" + roleList +'}';}public List<Role> getRoleList() {return roleList;}public void setRoleList(List<Role> roleList) {this.roleList = roleList;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public List<Orders> getOrdersList() {return ordersList;}public void setOrdersList(List<Orders> ordersList) {this.ordersList = ordersList;}
}
package com.lagou.domain;public class Role {private Integer id;private String rolename;private String roleDesc;@Overridepublic String toString() {return "Role{" +"id=" + id +", rolename='" + rolename + '\'' +", roleDesc='" + roleDesc + '\'' +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getRolename() {return rolename;}public void setRolename(String rolename) {this.rolename = rolename;}public String getRoleDesc() {return roleDesc;}public void setRoleDesc(String roleDesc) {this.roleDesc = roleDesc;}
}
一对一(多对一)multitable :
一对一查询模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
一对一查询语句:
SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis_db?characterEncoding=utf8
#如果是本机,localhost:3306可以省略,但后面的/并没有省略,所以上面需要是/mybatis_db
jdbc.username=root
jdbc.password=123456
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!--加载properties文件--><properties resource="jdbc.properties"></properties><!--设置别名--><typeAliases><package name="com.lagou.domain"/></typeAliases><!-- 顺序问题,需要放在environments元素前面--><plugins><plugin interceptor="com.github.pagehelper.PageHelper"><property name="dialect" value="mysql"/><!--dialect指定方言,因为不同数据库的对应分页操作不同,如mysql是limit--></plugin></plugins><environments default="development"><environment id="development"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource><!--上面决定了驱动,连接信息,即这些信息,给对应连接池操作,给出对应连接--></environment></environments><!--引入映射配置文件--><mappers><package name="com.lagou.mapper"></package></mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.lagou.mapper.OrderMapper"><!--一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息--><resultMap id="orderMap" type="com.lagou.domain.Orders"><id property="id" column="id"/><result property="ordertime" column="ordertime"/><result property="total" column="total"/><result property="uid" column="uid"/><!--可以多次对应,包括collection,他们之间也可嵌套(包括自己)association : 在进行一对一关联查询配置时,使用association标签进行关联通常操作的是对应实体类property="user" :要封装实体的属性名javaType="com.lagou.domain.User" 要封装的实体的属性类型这个属性可以不写,因为没有的话,默认property的user类型,但写了,就会按照写的
其他的属性基本也是如此操作
而resultType的必须写,因为没有默认只识别resultType,没有的话,就会报错--><association property="user" javaType="com.lagou.domain.User"><id property="id" column="uid"></id><!--uid与用户表的id是一样的,如果写id的话,不知道是哪个表的id,一般取最左边的--><result property="username" column="username"></result><result property="birthday" column="birthday"></result><result property="sex" column="sex"></result><result property="address" column="address"></result></association></resultMap><!--想要完成映射封装,默认的封装基本封装不了Orders的user变量里面去,所以需要自定义封装--><select id="findAllWithUser" resultMap="orderMap">SELECT * FROM orders o LEFT JOIN USER u ON o.uid = u.id</select></mapper>
package com.lagou.mapper;import com.lagou.domain.Orders;import java.util.List;public interface OrderMapper {/*一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息*/public List<Orders> findAllWithUser();}
package com.lagou.test;import com.lagou.domain.Orders;
import com.lagou.domain.User;
import com.lagou.mapper.OrderMapper;
import com.lagou.mapper.UserMapper;
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;
import java.util.List;public class mybatisTest {/*一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息*/@Testpublic void test1() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);List<Orders> orders = mapper.findAllWithUser();for (Orders order : orders) {System.out.println(order);}sqlSession.close();}
}
一对多:
一对多查询模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
一对多查询语句:
SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM orders o RIGHT JOIN USER u ON o.uid = u.id;
-- 或者
SELECT *,o.id oid FROM USER u LEFT JOIN orders o ON u.`id` = o.`uid`
-- 其中前面的*就是代表所有了,那么后面的就是起一个别名
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.lagou.mapper.UserMapper"><!--一对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息--><resultMap id="userMap" type="com.lagou.domain.User"><id property="id" column="id"></id><result property="username" column="username"></result><result property="birthday" column="birthday"></result><result property="sex" column="sex"></result><result property="address" column="address"></result><!--可以多次对应,包括association,他们之间也可嵌套(包括自己)collection : 一对多使用collection标签进行关联通常操作的是集合ofType泛型类型注意:在上面相同的情况下,对应下面的信息会发在一个集合里因为会检查,具体描述,当上面的字段内容都相同时,进行去重,即对应的下面内容放一个集合(合并)
不相同,则不会,若上面使用了对应id标签,那么就会对id进行去重,一样的,下面内容放一个集合
这个集合是第一个查到的没有去重的集合,那么说明,当出现第二个满足去重的集合时
将下面的内容放入第一个集合中,形成合并,而不相同的,可以将两个表的主键去掉,自己实验一下,我通过实验
只有当上面没有对应id标签,且上面字段内容有不一样的,那么后台查询时,会出现两个相同的id(因为条件是判断id的)--><collection property="ordersList" ofType="com.lagou.domain.Orders"><id property="id" column="oid"></id><!--通过起别名,使得同样的字段,不同了--><result property="ordertime" column="ordertime"/><result property="total" column="total"/><result property="uid" column="uid"/></collection></resultMap><select id="findAllWithOrder" resultMap="userMap">SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM orders o RIGHT JOIN USER u ON o.uid = u.id</select>
</mapper>
package com.lagou.mapper;import com.lagou.domain.User;import java.util.List;public interface UserMapper {/*一对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息*/public List<User> findAllWithOrder();
}
/*一对多关联查询:查询所有用户及关联的订单信息*/@Testpublic void test2() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> allWithOrder = mapper.findAllWithOrder();for (User user : allWithOrder) {System.out.println(user);}sqlSession.close();}
多对多:
多对多查询的模型
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
多对多查询语句
SELECT u.*,r.id rid,r.rolename,r.roleDesc FROM USER u LEFT JOIN sys_user_role ur ON ur.userid = u.idLEFT JOIN sys_role r ON ur.roleid = r.id-- 可以在上一个查询结果前,再次进行查询,from起作用,连接,select然后查询
<!--多对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息--><resultMap id="userRoleMap" type="user"><id property="id" column="id"/><result property="username" column="username"></result><result property="birthday" column="birthday"></result><result property="sex" column="sex"></result><result property="address" column="address"></result><collection property="roleList" ofType="role"><!--如果没有值,那么就是空值,而不是null--><id column="rid" property="id"></id><result column="rolename" property="rolename"></result><result column="roleDesc" property="roleDesc"></result></collection></resultMap><select id="findAllWithRole" resultMap="userRoleMap">SELECT u.*,r.id rid,r.rolename,r.roleDesc FROM USER u LEFT JOIN sys_user_role ur ON ur.userid = u.idLEFT JOIN sys_role r ON ur.roleid = r.id</select>
/*多对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息*/public List<User> findAllWithRole();
/*多对多关联查询:查询所有用户及关联的角色信息*/@Testpublic void test3() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> allWithRole = mapper.findAllWithRole();for (User user : allWithRole) {System.out.println(user);}sqlSession.close();}
小结:
MyBatis 多表配置方式 :
/*
一对一配置:使用<resultMap>+<association>做配置 一对多配置:使用<resultMap>+<collection>做配置 多对多配置:使用<resultMap>+<collection>做配置 多对多的配置跟一对多很相似,难度在于SQL 语句的编写
*/
MyBatis 嵌套查询:
什么是嵌套查询 :
嵌套查询就是将原来多表查询中的联合查询语句拆成单个表的查询,再使用 mybatis 的语法嵌套在一 起
/*
如
需求:查询一个订单,与此同时查询出该订单所属的用户
联合查询:
SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;
嵌套查询:
先查询订单
SELECT * FROM orders
再根据订单uid 外键,查询用户
SELECT * FROM `user` WHERE id = #{根据订单查询的 uid}
最后使用mybatis,将以上二步嵌套起来 */
一对一嵌套查询 :
需求:查询一个订单,与此同时查询出该订单所属的用户
sql语句:
//SELECT * FROM orders
//SELECT * FROM user WHERE id = #{id}
/*根据id查询用户*/public User findById(Integer id);
/*一对一嵌套查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息*/public List<Orders> findAllWithUser2();
<resultMap id="orderMap2" type="com.lagou.domain.Orders"><id property="id" column="id"/><result property="ordertime" column="ordertime"/><result property="total" column="total"/><result property="uid" column="uid"/><!--问题:1.怎么去执行第二条sql , 2.如何执行第二条sql的时候,把uid作为参数进行传递那么解决这些问题,主要是执行对应sql,那么这里有个属性select,可以进行对应sql语句在对应元素对象里,会再次进行对应元素对象的执行,并将colimn的值作为参数传递给对应的sql语句使得最终所有对应数据都在对应对象里面即MappedStatement那么就可以将执行的sql语句的结果,封装到对应的user变量里--><!--先弄好User,然后赋值给user--><association property="user" javaType="com.lagou.domain.User"select="com.lagou.mapper.UserMapper.findById" column="uid" /><!--uid是数据库的字段,所以大小写可以忽略写-->
<!--由于下面语句查询结果里面,没有对应的字段,那么只能使用其他语句的数值了上下两次不同的封装,但都是变量//这里要说明一下,如果没有指定uid,那么不会操作自动了,因为使用了
//所以这里就写上了 <result property="uid" column="uid"/>--></resultMap><!--一对一嵌套查询--><select id="findAllWithUser2" resultMap="orderMap2">SELECT * FROM orders</select>
<!--根据id查询用户--><select id="findById" resultType="com.lagou.domain.User" parameterType="int" >SELECT * FROM user WHERE id = #{id}</select>
/*一对一嵌套查询:查询所有订单及关联的用户信息*/@Testpublic void test4() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);List<Orders> allWithUser2 = mapper.findAllWithUser2();for (Orders orders : allWithUser2) {System.out.println(orders);}sqlSession.close();}
实际上最后执行的user是javaType的user
一对多嵌套查询 :
需求:查询一个用户,与此同时查询出该用户具有的订单
sql语句:
SELECT * FROM USER
SELECT * FROM orders WHERE uid = #{uid}
<!--一对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息--><resultMap id="userOrderMap" type="com.lagou.domain.User"><id property="id" column="id"/><result property="username" column="username"></result><result property="birthday" column="birthday"></result><result property="sex" column="sex"></result><result property="address" column="address"></result><!--association和collection都可以使用select来再次进行语句执行,将结果赋值,一般的,都会将id作为参数当然了,使用select和column通常情况下,不会再写sql语句,但若写的话则会使用对应sql语句的结果,即自己写的不会进行赋值对于association来说,字段赋值时,只会操作一个类,进行赋值,所以使用对应语句的话,基本全是该语句的赋值对于collection来说,一般的会进行去重,但是使用对应sql语句,那么进行赋值时,没有去重(没有判断了)他们的操作,都是在对应MappedStatement对象里操作,当指定对应位置时,就会通过反射执行对应语句而下面的操作,实际上可以看成内部嵌套一个这个对象,再次反射执行,形成一个赋值,然后返回--><collection property="ordersList" ofType="com.lagou.domain.Orders" column="id"select="com.lagou.mapper.OrderMapper.findByUid" ></collection></resultMap><select id="findAllWithOrder2" resultMap="userOrderMap">SELECT * FROM USER</select>
<select id="findByUid" parameterType="int" resultType="com.lagou.domain.Orders">SELECT * FROM orders WHERE uid = #{uid}</select>
/*一对多嵌套查询:查询所有用户及关联的订单信息*/@Testpublic void test5() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> allWithOrder2 = mapper.findAllWithOrder2();for (User user : allWithOrder2) {System.out.println(user);// 要用到该用户的订单信息// System.out.println(user.getOrdersList());}sqlSession.close();}
/*一对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息*/public List<User> findAllWithOrder2();
/*根据uid查询对应订单*/public List<Orders> findByUid(Integer uid);
多对多嵌套查询:
需求:查询用户同时查询出该用户的所有角色
sql语句:
SELECT * FROM USER
SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON ur.roleid = r.idWHERE ur.userid = #{uid}-- 对于数据来说,多对多其实也是一个一对多,但是对于表来说,就是多对多
<resultMap id="userRoleMap2" type="com.lagou.domain.User"><id property="id" column="id"/><result property="username" column="username"></result><result property="birthday" column="birthday"></result><result property="sex" column="sex"></result><result property="address" column="address"></result><!--注意:使用对应sql,就是自动赋值了,使用对应set方法执行
参数就是赋值的参数,没有对应的当然就是默认了且会根据变量直接赋值,在Mybatis里是这样的,Mybatis会直接赋值,而一般的是不会的实际上都是一个新的对象,所以没赋值就是默认值--><collection property="roleList" ofType="com.lagou.domain.Role" column="id" select="com.lagou.mapper.RoleMapper.findByUid"></collection><!--这个感觉就像没有去重一样,直接将查询的所有结果封装好--></resultMap><!--多对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息--><select id="findAllWithRole2" resultMap="userRoleMap2">SELECT * FROM USER</select>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.lagou.mapper.RoleMapper"><select id="findByUid" resultType="com.lagou.domain.Role" parameterType="int">SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON ur.roleid = r.idWHERE ur.userid = #{uid}</select>
</mapper>
/*根据用户id查询对应角色*/public List<Role> findByUid(Integer uid);
/*多对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息*/public List<User> findAllWithRole2();
/*多对多嵌套查询:查询所有用户及关联的角色信息
*/@Testpublic void test6() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> allWithRole2 = mapper.findAllWithRole2();for (User user : allWithRole2) {System.out.println(user);}sqlSession.close();}
小结:
/*
一对一配置:使用<resultMap>+<association>做配置,通过column 条件,执行 select 查询 一对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询 多对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询 优点:简化多表查询操作 缺点:执行多次sql 语句,浪费数据库性能 */
最后:自动赋值一般是不会报错的
只是没有找到对应的set方法(没有对应set方法,Mybatis会直接赋值),不会进行操作而已
62-Mybatis高级介绍相关推荐
- 1. MyBatis框架介绍
1. MyBatis框架介绍 1. 框架概述 程序开发中框架(framework)往往是对常见功能的封装,可以把框架理解为软件的设计规范或者标准化的组件,好比机械中的螺丝螺母等标准的机械部件 假如你要 ...
- 2.4.3 Mybatis 高级查询, 复杂映射, 返回主键, 动态SQL if, set, foreach, 核心配置文件深入,plugins标签, 多表查询, 嵌套查询
目录 Mybatis 复杂映射&配置文件深入 一 Mybatis高级查询 1.1 ResutlMap属性 1.2 多条件查询(三种) 1.3 模糊查询 二 Mybatis映射文件深入 2.1 ...
- Java MyBatis的介绍及其执行原理
写在前面 ??MyBatis学习 ??今天我们进行MyBatis框架的学习,认识MyBatis及其执行原理,感谢你的阅读,内容若有不当之处,希望大家多多指正,一起进步!!! 如果觉得博主文章还不错,可 ...
- mybatisplus 结果_Java之MyBatis Plus介绍
Java之MyBatis Plus介绍 1.MyBatis Plus 介绍 MyBatis Plus 是国内人员开发的 MyBatis 增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开 ...
- SpringBoot整合Mybatis(高级)
SpringBoot整合Mybatis(高级) 文章目录 SpringBoot整合Mybatis(高级) 前言 基础环境配置 增删改查 ResultMap 复杂查询 多对一 一对多 动态SQL if ...
- MyBatis-学习笔记01【01.Mybatis课程介绍及环境搭建】
Java后端 学习路线 笔记汇总表[黑马程序员] MyBatis-学习笔记01[01.Mybatis课程介绍及环境搭建][day01] MyBatis-学习笔记02[02.Mybatis入门案例] M ...
- MyBatis JdbcType介绍
MyBatis JdbcType介绍 JdbcType介绍 数据库列字段都是有类型的,不同的数据库有不同的类型.为了表示这些数据类型,Java源码是采用枚举来定义的: public enum JDBC ...
- 84-MongoDB高级介绍
MongoDB高级介绍 通过上一章博客的学习,基本上明白了mongodb的基础和操作,接下来我们来操作mongodb的高级部分 搭建高可用集群: MongoDB主从复制架构原理和缺陷: 在主从结构中, ...
- 【Mybatis高级映射】一对一映射、一对多映射、多对多映射
前言 当我们学习heribnate的时候,也就是SSH框架的网上商城的时候,我们就学习过它对应的高级映射,一对一映射,一对多映射,多对多映射.对于SSM的Mybatis来说,肯定也是差不多的.既然开了 ...
最新文章
- Hibernate基本配置
- 硬件:RS232、RS422和RS485的区别
- gdb vscode 不进入断点_VScode配置MASM32运行环境(断点/运行/debug/配合emu8086(非DOSBox))...
- Python 之详解深拷贝和浅拷贝
- Pr视频剪辑软件使用小结
- 「IT基础」计算机网络结构
- 网站ICP备案和公安备案流程
- 开源分布式数据库中间件 DBLE
- forEach() map()— —更新数组 filter()、includes()、find()、findIndex()— —筛选(删除)数组 some()、every()— 判断数组 reduce
- SaaS模式和传统软件模式有什么区别?
- python推理拟合函数
- NVIDIA 3D VISION 在戴尔Alienware/XPS系列上的使用
- 信息系统项目管理师笔记
- Qt中的JSON操作_1: JSON的基本知识介绍(JSON格式、JSON数组、JSON对象、应用场景)
- 计算机科学与技术博士招生人数,南京大学2017年计算机科学与技术系博士招生目录.pdf...
- 音乐多媒体播放的三种方式
- 彻底解决快压这个垃圾流氓软件
- CVPR2022新作:P图不会,深度学习来帮忙,基于GAN逆映射的图像编辑(中)
- openVAS 介绍
- 目标是买车买房白富美
热门文章
- JSD-2204-创建Spring项目-Day19
- 数据挖掘:Apriori(先验)算法
- 投稿论文图片分辨率达不到要求的解决方案
- flutter type ‘int‘ is not a subtype of type ‘String?‘
- 当Excel遇到在线表格,看如何轻松解决办公难题 ?
- 双色汉诺塔算法的证明——数学归纳法
- progress的高级过程调用以及全局变量
- SQLmap Tamper编写方法(笔记)
- python中的函数
- 命题公式的主合取范式C语言,用C或C++编写程序,要求:输入命题公式,给出它的主合取范式和主析取范式....