0%

MyBatis色扣

SQL联合查询

环境准备

Person对象/表:id(Integer,主键),name(String),car(对象,表中为carid)

Car对象/表:carId(Integer,外键),carName

一个PersonMapper接口,一个getPersonById(int id)方法

准备好数据

person car

情景一:查某人以及他买的车

SQL语句

1
SELECT
2
    p.id,
3
    p.name,
4
    p.car_id car_id,
5
    c.car_name car_name
6
FROM
7
    person p
8
    LEFT JOIN car c ON p.car_id = c.car_id
9
WHERE
10
    id=#{id}

查询结果

映射文件

自定义封装规则:级联属性封装结果

1
<resultMap id="pandc" type="com.czw.bean.Person">
2
    <id property="id" column="id"/>
3
    <result property="name" column="name"/>
4
    <result property="car.carId" column="car_id"/>
5
    <result property="car.carName" column="car_name"/>
6
</resultMap>

测试方法

1
@Test
2
@SneakyThrows
3
public void testPC(){
4
    @Cleanup SqlSession ss=sqlSessionFactory.openSession(true);
5
    IPersonMapper pm=ss.getMapper(IPersonMapper.class);
6
    Person person=pm.getPersonById(1);
7
    System.out.println(person);
8
}

结果完美

细节完善

使用MyBatis推荐的association标签封装复杂bean对象,property指定哪个元素是对象,javaType指定封装类型

1
<association property="car" javaType="com.czw.bean.Car">
2
    <id property="carId" column="car_id"/>
3
    <result property="carName" column="car_name"/>
4
</association>

用这一段替换级联属性封装,测试同样成功

情景二:查买某种车的那批人

SQL语句

1
SELECT
2
	c.car_id,
3
	c.car_name,
4
	p.id person_id,
5
	p.name person_name
6
FROM
7
	car c
8
LEFT JOIN
9
	person p ON c.car_id=p.car_id
10
WHERE
11
	c.car_id = #{id}

配置文件

使用collection标签:property指定哪个元素是集合,ofType:指定集合元素类型

1
<resultMap id="listperson" type="com.czw.bean.Car">
2
    <id property="carId" column="car_id"/>
3
    <result property="carName" column="car_name"/>
4
    <collection property="persons" ofType="com.czw.bean.Person">
5
        <id property="id" column="person_id"/>
6
        <result property="name" column="person_name"/>
7
    </collection>
8
</resultMap>

测试方法

1
@Test
2
@SneakyThrows
3
public void testCP(){
4
    @Cleanup SqlSession ss=sqlSessionFactory.openSession(true);
5
    ICarMapper cm=ss.getMapper(ICarMapper.class);
6
    Car car= cm.getCarById(1);
7
    System.out.println(car);
8
    List<Person> persons = car.getPersons();
9
    for (Person person:persons) {
10
        System.out.println(person);
11
    }
12
}

结果同样完美

分步查询

情景一

SQL拆分

将表连接分为两个简单SQL语句查询,一个查person,一个查car,自定义封装时,在association标签里指定select的值为查car的select标签id(最好带上全类名),指定colume为要传入的参数,这样MyBatis就会自动去查car然后封装好。

CarMapper.xml

1
<select id="getCarById2" resultType="com.czw.bean.Car">
2
    select * from car where car_id=#{carId}
3
</select>

PersonMapper.xml

1
<select id="getPersonById2" resultMap="pc2">
2
    select * from person where id=#{id}
3
</select>
4
5
<resultMap id="pc2" type="com.czw.bean.Person">
6
    <id property="id" column="id"/>
7
    <result property="name" column="name"/>
8
    <association property="car" select="com.czw.mapper.ICarMapper.getCarById2" column="car_id"></association>
9
</resultMap>

结果也成功了,只不过用了两句SQL。

这样有一个问题,就是不需要查car的时候,也会去查,这时候就要全局开启按需加载策略

细节优化

1
<!-- 开启延时加载策略-->
2
<setting name="lazyLoadingEnabled" value="true"/>
3
<!-- 开启属性按需加载策略-->
4
<setting name="aggressiveLazyLoading" value="false"/>

这样只有要用到时,才会用后置SQL的去查出信息,可以在association标签的fetchType属性设置为eager覆盖全局配置文件的懒加载,设为lazy相反

情景二

SQL拆分

一个根据car_id查出所有person的返回List的SQL

1
<select id="getPersonByCarId" resultType="com.czw.bean.Person">
2
    select * from person where car_id=#{id}
3
</select>

一个根据car_id查到car的SQL,后置调用上面那句

1
<select id="getCarById22" resultMap="cp2">
2
    select * from car where car_id=#{carId}
3
</select>
4
<resultMap id="cp2" type="com.czw.bean.Car">
5
    <id property="carId" column="car_id"/>
6
    <result property="carName" column="car_name"/>
7
    <collection property="persons" select="com.czw.mapper.IPersonMapper.getPersonByCarId" column="car_id"></collection>
8
</resultMap>

结局完美

综合分析

分步查询了解即可,推荐使用连接查询

动态SQL

简化SQL语句动态拼串

环境搭建

if标签

准备一个以Student对象为参数的方法,根据对象设置的属性进行动态拼接查询

1
<select id="getStuBySome" resultMap="stuMap">
2
    select * from student where
3
    <if test="age!=null">
4
        <!--age不为空时,添加age>=参数对象的年龄的条件-->
5
        age >= #{age} and
6
    </if>
7
    <if test="!gender.equals('') and gender!=null">
8
        <!--gender不为空并不是空串时,添加gender=参数对象的性别的条件-->
9
        gender = #{gender}
10
    </if>
11
</select>

测试方法

1
@Test
2
@SneakyThrows
3
public void testIF(){
4
    @Cleanup SqlSession ss=sqlSessionFactory.openSession(true);
5
    IStudentMapper sm=ss.getMapper(IStudentMapper.class);
6
    Student student=new Student();
7
    //student.setAge(19);
8
    //这句不注释就会查出加了年龄限制的数据,实现了动态查询
9
    student.setGender("男");
10
    List<Student> studentList = sm.getStuBySome(student);
11
    for (Student stu:studentList) {
12
        System.out.println(stu);
13
    }
14
}

运行结果完美

注释时查到了所有性别为男的数据,不注释时查到了性别为男切年龄大于等于19的数据

where标签

上述SQL语句中,要是没有条件就会多出一个where来,要是没有最后一个条件,就会多出一个and来,所以将条件写在where标签中,并且将and写前面,MyBatis会自动去除前面的多余and,改成这样。

1
<select id="getStuBySome" resultMap="stuMap">
2
    select * from student
3
    <where>
4
        <if test="age!=null">
5
            and age >= #{age}
6
        </if>
7
        <if test="!gender.equals('') and gender!=null">
8
            and gender = #{gender}
9
        </if>
10
    </where>
11
</select>

当两个条件都写了时,MyBatis去除了age条件那个and,测试成功

trim标签

1
<trim prefix="where" suffixOverrides="and">
2
    <if test="age!=null">
3
        age >= #{age} and
4
    </if>
5
    <if test="!gender.equals('') and gender!=null">
6
        gender = #{gender} and
7
    </if>
8
</trim>

同样不写where,用这个标签的prefix属性给下面这坨整体加一个where前缀,suffixOverrides可以将多余的后缀去除,这样可以去除后面的and,还有suffix加后缀,和prefixOverrides去除多余前缀,个人觉得还不如用where+and写前面。

foreach标签

当输入的参数是一个集合时,用这个标签

collection指定输入的集合名,item指定每一个元素,separator指定分隔符,open指定开始符,close指定结束符。

1
<select id="getStuByIds" resultMap="stuMap">
2
    select * from student where id in
3
    <foreach collection="ids" item="id" separator="," open="(" close=")">
4
        #{id}
5
    </foreach>
6
</select>

测试方法

1
@Test
2
@SneakyThrows
3
public void testFor(){
4
    @Cleanup SqlSession ss=sqlSessionFactory.openSession(true);
5
    IStudentMapper sm=ss.getMapper(IStudentMapper.class);
6
    List<Student> studentList = sm.getStuByIds(Arrays.asList(1,2));
7
    for (Student stu:studentList) {
8
        System.out.println(stu);
9
    }
10
}

结果得到了id为1和2的student,完美

choose标签

分支选择,相当于if-else

1
<select id="getStuByChoose" resultMap="stuMap">
2
    select * from student
3
    <where>
4
        <choose>
5
            <when test="id!=null">
6
                id=#{id}
7
            </when>
8
            <when test="age!=null">
9
                age=#{age}
10
            </when>
11
            <when test="gender!=null and !gender.equals('')">
12
                gender=#{gender}
13
            </when>
14
            <otherwise>
15
                1=1
16
            </otherwise>
17
        </choose>
18
    </where>
19
</select>

测试方法

1
@Test
2
@SneakyThrows
3
public void testChoose(){
4
    @Cleanup SqlSession ss=sqlSessionFactory.openSession(true);
5
    IStudentMapper sm=ss.getMapper(IStudentMapper.class);
6
    Student student=new Student();
7
    //student.setId(1);有id查id
8
    //student.setAge(19);有年龄查年龄
9
    //student.setGender("男");有性别查性别
10
    //都没有全查出来
11
    List<Student> studentList = sm.getStuByChoose(student);
12
    for (Student stu:studentList) {
13
        System.out.println(stu);
14
    }
15
}

结果同样完美,在这里,与if标签不同的时,choose在满足第一个when的时候就不会继续看下面的,如果都不满足,就看otherwise

set标签

写在update里,替换原生uptade语句,与if结合使用,可以写强大的动态更新。set标签能自动去除多余的逗号,加上if标签有哪个字段就更新哪个字段。

1
<update id="updateStu">
2
    update student
3
    <set>
4
        <if test="name!=null and !name.equals('')">
5
            sname=#{name},
6
        </if>
7
        <if test="age!=null and age>0">
8
            age=#{age},
9
        </if>
10
        <if test="gender!=null and !gender.equals('')">
11
            gender=#{gender},
12
        </if>
13
        <if test="email!=null and !email.equals('')">
14
            email=#{email},
15
        </if>
16
    </set>
17
    <where>
18
        id=#{id}
19
    </where>
20
</update>

测试方法

1
@Test
2
@SneakyThrows
3
public void testSet(){
4
    @Cleanup SqlSession ss=sqlSessionFactory.openSession(true);
5
    IStudentMapper sm=ss.getMapper(IStudentMapper.class);
6
    Student student=new Student();
7
    student.setId(3);
8
    student.setAge(20);
9
    student.setEmail("ccc@czw.com");
10
    System.out.println(student);
11
    //student.setGender("男");
12
    sm.updateStu(student);
13
}

结果完美,只更新了年龄和email

OGNL表达式

bind标签

将一个表达式的值到一个变量,不太好用

总结

头发-1