MyBatis学习2


  

8 Mapper动态代理

  在前面例子中自定义Dao接口实现类时发现一个问题:Dao的实现类其实并没有干什么实质性的工作,它仅仅就是通过SqlSession的相关API定位到映射文件mapper中相应id的SQL语句,真正对DB进行操作的工作其实是由框架通过mapper中的SQL完成的。
  所以,MyBatis框架就抛开了Dao的实现类,直接定位到映射文件mapper中的相应SQL语句,对DB进行操作。这种对Dao的实现方式称为Mapper的动态代理方式。
  Mapper动态代理方式无需程序员实现Dao接口。接口是由MyBatis结合映射文件自动生成的动态代理实现的。

8.1 映射文件的namespace属性值

  一般情况下,一个Dao接口的实现类方法使用的是同一个SQL映射文件中的SQL映射id。所以,MyBatis框架要求,将映射文件中mapper标签的namespace属性设为Dao接口的全类名,则系统会根据方法所属Dao接口,自动到相应namespace的映射文件中查找相关的SQL映射。
  简单来说,通过接口名即可定位到映射文件mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chengzi.dao.IStudentDao">
    <insert id="insertStudent" parameterType="com.chengzi.beans.Student">
        insert into student(name,age,score) values (#{name},#{age},#{score})
    </insert>

    <insert id="insertStudentCachId" parameterType="com.chengzi.beans.Student">
        insert into student(name,age,score) values (#{name},#{age},#{score})
        <selectKey resultType="int" keyProperty="id" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
    </insert>

    <delete id="deleteStudentById">
        delete from student where id = #{id}
    </delete>

    <delete id="updateStudent">
        update student set name = #{name},age=#{age},score=#{score} where id = #{id}
    </delete>
    
    <select id="selectAllStudents" resultType="com.chengzi.beans.Student">
        select * from student
    </select>

    <select id="selectStudentById" resultType="com.chengzi.beans.Student">
        select * from student where id = #{**}
    </select>
    
    <select id="selectStudentByName" resultType="com.chengzi.beans.Student">
        select * from student where name like '%'#{ooo}'%'
    </select>

    <select id="selectStudentByMap" resultType="com.chengzi.beans.Student">
        select * from student where id between #{studentId} and #{stu.id}
    </select>
    
    <resultMap id="studenteMapper" type="com.chengzi.beans.Student">
        <id property="id" column="t_id"/>
        <id property="name" column="t_name"/>
        <id property="age" column="t_age"/>
    </resultMap>

    <select id="selectStudentById2" resultMap="studenteMapper">
        select
            t_id,t_name,t_age,score
        from student where t_id = #{**}
    </select>
</mapper>

8.2 修改日志输出控制文件

mapper的namespace修改了,则需要将日志输出控制文件中logger的输出对象进行修改。

#A根日志
#log4j.rootLogger=debug,console
#打印mybatis的sql日志更简单
log4j.logger.com.chengzi.dao.IStudentDao=debug,console

#A定义一个控制台日志附加器
log4j.appender.console=org.apache.log4j.ConsoleAppender
#控制输出到控制台使用的目录:Target,可以使用System.out或Sytem.err
log4j.appender.console.Target=System.out
#A指定附加器控制输出的日志
log4j.appender.console.layout=org.apache.log4j.PatternLayout
#A定义附加器日志输出的格式,-表示打印的内容向左对齐
log4j.appender.console.layout.ConversionPattern=[log4j-demo] %-d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] %c:%l - %m%n

8.3 Dao接口方法名

  MyBatis框架要求,接口中的方法名,与映射文件中相应的SQL标签的id值相同。系统会自动根据方法名到相应的映射文件中查找同名的SQL映射id。简单来说,通过方法名就可定位到映射文件mapper中相应的SQL语句。

public interface IStudentDao {

    //插入
    void insertStudent(Student studnet);

    void insertStudentCachId(Student student);

    //删除
    void deleteStudentById(int id);

    //改
    void updateStudent(Student student);

    //查询所有
    List<Student> selectAllStudents();
    List<Map<String, Object>> selectStudentMap();

    //查询指定学生
    Student selectStudentById(int id);
    List<Student> selectStudentByMap(Map<String,Object> map);

    //根据姓名查询
    List<Student> selectStudentByName(String name);

    Student selectStudentById2(int id);
}

8.4 Dao对象的获取

  使用时,只需调用SqlSession的getMapper()方法,即可获取指定接口的实现类对象。该方法的参数为指定Dao接口类的class值。

public class MyTest2 {
    private IStudentDao iStudentDao;
    private SqlSession sqlSession;

    @Before
    public void before() throws IOException {
        sqlSession = MybatisUtils.getSqlSession();
        iStudentDao = sqlSession.getMapper(IStudentDao.class);
    }
    @Before
    public void before() throws IOException {
        sqlSession = MybatisUtils.getSqlSession();
        iStudentDao = sqlSession.getMapper(IStudentDao.class);
        sqlSession.commit();
    }
    @After
    public void after() {
        sqlSession.close();
    }
    //省略其他方法。。。
    
}

8.5 删除Dao实现类

  由于通过调用Dao接口的方法,不仅可以从SQL映射文件中找到所要执行SQL语句,还可通过方法参数及返回值,将SQL语句的动态参数传入,将查询结果返回。所以,Dao的实现工作,完全可以由MyBatis系统自动根据映射文件完成。所以,Dao的实现类就不再需要了。
  Dao实现对象是由JDK的Proxy动态代理自动生成的。
在这里插入图片描述

8.6 删除selectStudentMap()方法测试

  MyBatis框架对于Dao查询的自动实现,底层只会调用selectOne()与selectList()方法。而框架选择方法的标准是测试类中用于接收返回值的对象类型。若接收类型为List,则自动选择selectList()方法;否则,自动选择selectOne()方法。
  这里接收类型为Map,所以框架选择了selectOne()方法,会报错。所以这里需要删除这个selectStudentMap()方法的测试
在这里插入图片描述
在这里插入图片描述

8.7 返回类型为map

1、xml

<select id="selectStudentMap" resultType="map">
        select * from student
</select>

2、接口

Map<String, Student> selectStudentMap();

3、测试类

@Test
    public void testselectStudentMap() {
        List<Map<String, Object>> list = iStudentDao.selectStudentMap();
        for (Map<String, Object> l : list) {
            Set<String> strings = l.keySet();
            for (String k : strings){
                System.out.println( k);
                System.out.println(l.get(k));
            }
        }
    }

使用场合:按照年龄统计人数:

select count(*),age from student group age;

在这里插入图片描述

9 多查询条件无法整体接收问题的解决

  在实际开发中,表单中所给出的查询条件有时是无法将其封装为一个对象的,也就是说,查询方法只能携带多个参数,而不能携带将这多个参数进行封装的一个对象。对于这个问题,有两种解决方案。
(1)将这多个参数封装为一个Map
   前面已经用过;
(2)多个参数逐个接收
  对于mapper中的SQL语句,可以通过参数索引#{index}的方式逐个接收每个参数

<select id="selectStudentsByConditions" resultType="com.chengzi.beans.Student">
        select * from student where name like '%' #{0} '%'
                                    and age &gt; #{1}
</select>
List<Student> selectStudentsByConditions(String n, int i);
	@Test
    public void testSelectStudentsByConditions() {
        List<Student> students = iStudentDao.selectStudentsByConditions("n",20);
    }

10 动态SQL

  动态SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的SQL语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的SQL语句。此时,可使用动态SQL来解决这样的问题。

动态SQL,即通过MyBatis提供的各种标签对条件作出判断以实现动态拼接SQL语句。这里的条件判断使用的表达式为OGNL表达式。常用的动态SQL标签有iF、where、choose、foreach等。有一个有意思的发现是,MyBatis的动态SQL语句,与JSTL中的语句非常相似。

1、测试数据
在这里插入图片描述
2、注意事项
  在mapper的动态SQL中若出现大于号(>)、小于号(<)、大于等于号(>=),小于等于号(<=)等符号,最好将其转换为实体符号。否则,XML可能会出现解析出错问题。特别是对于小于号(<),在XML中是绝对不能出现的。否则,一定出错。

原符号替换符号
<&lt ;
<=&lt ;=
>&gt ;
>=&gt ;=
&&amp ;
&apos ;
"&quot ;

10.1 IF标签

  对于该标签的执行,当test的值为true时,会将其包含的SQL片断拼接到其所在的SQL语句中。
  本例实现的功能是:查询出满足用户提交查询条件的所有学生。用户提交的查询条件可以包含一个姓名的模糊查询,同时还可以包含一个年龄的下限。当然,用户在提交表单时可能两个条件均做出了设定,也可能两个条件均不做设定,也可以只做其中一项设定。
  这引发的问题是,查询条件不确定,查询条件依赖于用户提交的内容。此时,就可使用动态SQL语句,根据用户提交内容对将要执行的SQL进行拼接。
1、xml

<select id="selectStudentIf" resultType="com.chengzi.beans.Student">
        select * from student where 1=1
        <if test="name != null and name != '' ">
            and name like '%' #{name} '%'
        </if>
        <if test="age > 0 ">
            and age > #{age}
        </if>
</select>

2、dao接口

List<Student> selectStudentIf(Student student);

3、测试类

	@Test
    public void testSelectStudentsIf() {
        Student student = new Student("",22,95);
        List<Student> students = iStudentDao.selectStudentIf(student);
        System.out.println(students);
    }

10.2 WHERE标签

  if标签的中存在一个比较麻烦的地方:需要在where后手工添加1=1的子句。因为,若where后的所有if条件均为false,而where后若又没有1=1子句,则SQL中就会只剩下一个空的where,SQL出错。所以,在where后,需要添加永为真子句1=1,以防止这种情况的发生。但当数据量很大时,会严重影响查询效率。

1、xml

<select id="selectStudentWhere" resultType="com.chengzi.beans.Student">
        select * from student
        <where>
            <if test="name != null and name != '' ">
                and name like '%' #{name} '%'
            </if>
            <if test="age > 0 ">
                and age > #{age}
            </if>
            <if test="score != null ">
                and age > #{age}
            </if>
        </where>
</select>
注意:
使用<where/>标签,在有查询条件时,可以自动添加上where子句;没有查询条件时,不会添加where子句。
需要注意的是,第一个<if/>标签中的SQL片断,可以不包含and。不过,写上and也不错,系统会自动将多出的and去掉。
但其它<if/>中SQL片断的and,必须要求写上。否则SQL语句将拼接出错。

3、测试类

	@Test
    public void testSelectStudentsWhere() {
        Student student = new Student("",22,95);
        List<Student> students = iStudentDao.selectStudentWhere(student);
        System.out.println(students);
    }

4、运行结果

[log4j-demo] 2019-09-15 09:34:49,543 [DEBUG] com.chengzi.dao.IStudentDao.selectStudentWhere:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:142) - ==>  Preparing: select * from student WHERE age > ? and age > ? 
[log4j-demo] 2019-09-15 09:34:49,573 [DEBUG] com.chengzi.dao.IStudentDao.selectStudentWhere:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:142) - ==> Parameters: 22(Integer), 22(Integer)
[log4j-demo] 2019-09-15 09:34:49,590 [DEBUG] com.chengzi.dao.IStudentDao.selectStudentWhere:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:142) - <==      Total: 5
[Student{id=1, name='张三', age=23, score=93.5}, Student{id=2, name='李四', age=24, score=94.5}, Student{id=3, name='王五', age=25, score=95.5}, Student{id=4, name='赵六', age=26, score=96.5}, Student{id=5, name='田七', age=27, score=97.5}]

10.3 CHOOSE标签

  该标签中只可以包含< when />< otherwise/ >,可以包含多个< when/ >与一个< otherwise/>。它们联合使用,完成Java中的开关语句switch…case功能。本例要完成的需求是,若姓名不空,则按照姓名查询;若姓名为空,则按照年龄查询;若没有查询条件,则没有查询结果。
1、xml

    <select id="selectStudentChoose" resultType="com.chengzi.beans.Student">
        select * from student
        <where>
            <choose>
                <when test="age != 0  ">
                    age >= #{age}
                </when>
                <when test="score != 0">
                    score >= #{score}
                </when>
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>
对于<choose/>标签,其会从第一个<when/>开始逐个向后进行条件判断。若出现<when/>中的test属性值
为true的情况,则直接结果<choose/>标签,不再向后进行判断查找。若所有<when/>的test判断结果均
为false,则最后会执行<otherwise/>标签。

2、接口

List<Student> selectStudentChoose(Student student);

3、测试类

	@Test
    public void testSelectStudentChoose() {
        Student student = new Student("",0,0);
        List<Student> students = iStudentDao.selectStudentChoose(student);
        System.out.println(students);
    }

10.4 ForEach标签

10.4.1 遍历Array

< foreach />标签用于实现对于数组与集合的遍历。对其使用,需要注意:
(1)collection表示要遍历的集合类型,这里是数组,即array。
(2)open、close、separator为对遍历内容的SQL拼接。
本例实现的需求是,查询出id为1与3的学生信息。
1、xml

	<select id="selectStudentForEachArray" resultType="com.chengzi.beans.Student">
        select * from student
        <if test="array != null and array.length > 0">
            where id in 
            <foreach collection="array" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>
动态SQL的判断中使用的都是OGNL表达式。OGNL表达式中的数组使用array表示,数组长度使用array.length表示
也就是说test后边只能写array,不能是其他

2、dao接口

List<Student> selectStudentForEachArray(Object[] studentIds);

3、测试类

	@Test
    public void testSelectStudentForEachArray() {
        Object [] studentIds = new Object[] {1,3};
        List<Student> students = iStudentDao.selectStudentForEachArray(studentIds);
        System.out.println(students);
    }

10.4.2 遍历List

1、xml

<select id="selectStudentForEachList" resultType="com.chengzi.beans.Student">
        select * from student
        <if test="list != null and list.size > 0">
            where id in
            <foreach collection="list" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
</select>

2、dao接口

List<Student> selectStudentForEachList(List<Integer> studentIds);

3、测试类

	@Test
    public void testSelectStudentForEachList() {
        List<Integer> studentIds = new ArrayList<>();
        studentIds.add(1);
        studentIds.add(3);
        List<Student> students = iStudentDao.selectStudentForEachList(studentIds);
        System.out.println(students);
    }

10.4.3 遍历自定义类型的List

1、xml

<select id="selectStudentForEachListCustom" resultType="com.chengzi.beans.Student">
        select * from student
        <if test="list != null and list.size > 0">
            where id in
            <foreach collection="list" open="(" close=")" item="stu" separator=",">
                #{stu.id}
            </foreach>
        </if>
</select>

2、dao接口

List<Student> selectStudentForEachListCustom(List<Student> studentIds);

3、测试类

	@Test
    public void testSelectStudentForEachListCustom() {
        List<Student> studentIds = new ArrayList<>();
        Student student = new Student();
        student.setId(1);
        Student student2 = new Student();
        student2.setId(3);

        studentIds.add(student);
        studentIds.add(student2);
        List<Student> students = iStudentDao.selectStudentForEachListCustom(studentIds);
        System.out.println(students);
    }

10.5 SQL标签

  < sql/>标签用于定义SQL片断,以便其它SQL标签复用。而其它标签使用该SQL片断,需要使用
< include/>子标签。该标签可以定义SQL语句中的任何部分,所以子标签可以放在动态SQL的任何位置。
1、xml

<sql id="selectHead">
    select * from student
</sql>
<select id="selectStudentBySqlFragment" resultType="com.chengzi.beans.Student">

    <include refid="selectHead"/>

    <if test="list != null and list.size > 0">
            where id in
        <foreach collection="list" open="(" close=")" item="stu" separator=",">
                #{stu.id}
        </foreach>
    </if>
</select>

2、dao接口

List<Student> selectStudentBySqlFragment(List<Student> studentIds);

3、测试类

	@Test
    public void testSelectStudentBySqlFragment() {
        List<Student> studentIds = new ArrayList<>();
        Student student = new Student();
        student.setId(1);
        Student student2 = new Student();
        student2.setId(3);

        studentIds.add(student);
        studentIds.add(student2);
        List<Student> students = iStudentDao.selectStudentBySqlFragment(studentIds);
        System.out.println(students);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值