MyBatis--单表的CURD操作(使用mapper动态代理)

单表的CURD操作(使用mapper动态代理)

MyBatis框架抛开dao的实现类,直接定位到映射文件mapper的相应sql语句,对DB进行操作。这种dao的实现方式成为mapper的动态代理方式。

mapper动态代理方式无需程序员实现dao接口。接口是由MyBatis结合映射文件自动生成的动态代理实现的。

1.映射文件的namespace属性值

一般情况下,一个dao接口的实现类方法使用的是同一个sql映射文件中的sql映射id。所以,MyBatis框架要求,将映射文件中<mapper/>标签的namespace属性设为dao接口的全类名,则系统会根据方法所属dao接口,自动到相应namespace的映射文件中查找相关的sql映射。

简单来说,通过接口名即可定位到映射文件mapper。

2.修改日志输出控制文件

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

##define a logger
#log4j.logger.namespace_value=trace,console
log4j.logger.com.hcx.dao.IStudentDao=trace,console

3.dao接口方法名

MyBatis框架要求,接口中的方法名,与映射文件中相应的sql标签的id值相同。系统会自动根据方法名到相应的映射文件中查找同名的sql映射id。

简单来说,通过方法名就可定位到映射文件mapper中相应的sql语句。

接口:

public interface IStudentDao {
    
    int insertStudent(Student student); 
    void deleteStudentById(int id);
    void updateStudent(Student student);
    
    List<Student> selectAllStudents();
    Map<String, Object> selectAllStudentsMap();
    
    Student selectStudentById(int id);
    List<Student> selectStudentsByName(String name);
    
}

映射:

<select id="selectStudnetById" parameterType="int" resultType="com.hcx.beans.Student">
    select id,name,age,score,birthday from student where id=#{id}
</select>

4.dao对象的获取

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

session = factory.openSession();
dao = session.getMapper(IStudentDao.class);

5.删除dao实现类

由于通过调用dao接口的方法,不仅可以从sql映射文件中找到所要执行sql语句,还可通过方法参数及返回值,将sql语句的动态参数传入,将查询结果返回。所以,dao的实现工作,完全可以由MyBatis系统自动根据映射文件完成。所以,dao的实现类就不再需要了。

dao实现对象是由jdk的proxy动态代理自动生成的。

6.测试类

1.在before注解方法中获取到Sqlsession对象后,通过Sqlsession的getMapper方法创建dao接口实现类的动态代理对象。在after注解方法中关闭Sqlsession对象。

public class MyTest {
    
    private IStudentDao dao;
    private SqlSession session;
    
    @Before
    public void setUp(){
        session = MyBatisUtils.getSqlSession();
        dao = session.getMapper(IStudentDao.class);
    }
    
    @After
    public void tearDown(){
        if(session!=null){
            session.close();
        }
    }
}

2.添加Sqlsession的提交方法

在增删改测试方法的最后,添加Sqlsession的commit方法,完成提交。

@Test
public void test01(){
    Student student = new Student("张三",23,99.8);
    dao.insertStudent(student);
    session.commit();
}

@Test
public void test02(){
    Student student = new Student("张三",23,99.8);
    dao.insertStudentCatchId(student);
    System.out.println("student="+student);
    session.commit();
}

@Test
public void test03(){
    dao.deleteStudentById(3);   
    session.commit();
}

3.删除selectStudentMap()方法测试

MyBatis框架对于dao查询的自动实现,底层只会调用selectOne与selectList()方法。而框架选择方法的标准是测试类中用户接收返回值得对象类型。若接收类型为list,则自动选择selectList()方法;否则,自动选择selectOne()方法。

接收类型为map,所以框架选择了selectOne()方法,会报错。

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

在实际工作中,表单中所给出的查询条件有时是无法将其封装为一个对象的,也就是说,查询方法只能携带多个参数,而不能携带将着这多个参数进行封装的一个对象。对于这个问题,有两种解决方案。

方案一:将这多个参数封装为一个map

将这多个参数封装为一个Map<String,Object>,根据Map进行查询。

1.dao接口

List<Student> selectStudentByMap(Map<String, Object> map);

2.测试类

@Test
public void test10(){
    Map<String, Object> map = new HashMap<String,Object>();
    map.put("nameCondition", "张");
    map.put("ageCondition",22);
    
    List<Student> students = dao.selectStudentByMap(map);
    for (Student student : students) {
        System.out.println(student);
    }
}

3.修改映射文件

<select id="selectStudentByMap" resultType="Student">
    select *from student
    where name like '%' #{nameCondition} '%'
    and age > #{ageCondition}
</select>

方案二:多个参数逐个接收

对于mapper中的SQL语句,可以通过参数索引#{index}的方式逐个接收每个参数。

1.dao接口

List<Student> selectStudentByConditions(String name,int age);

2.测试类

@Test
public void test11(){
    List<Student> students = dao.selectStudentByConditions("张", 22);
    for (Student student : students) {
        System.out.println(student);
    }
}

3.映射文件

<select id="selectStudentByconditions" resultType="Student">
    select * from student
    where name like '%' #{0} '%'
    and age > #{1}
</select>

动态SQL

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

用户自定义查询.PNG

动态sql,即通过MyBatis提供的各种标签对条件作出判断以实现拼接sql语句

这里的条件判断使用的表达式为OGNL表达式。常用的动态SQL标签有<if>、<where>、<choose/>、<foreach>等。

1.实体类:

public class Student {
    
    private Integer id;
    private String name;
    private int age;
    private double score;
    //无参构造器与带参构造器
    //getter 和 setter
    //toString()
}

2.测试类

public class MyTest {
    
    private IStudentDao dao;
    private SqlSession session;
    
    @Before
    public void setUp(){
        session = MyBatisUtils.getSqlSession();
        dao = session.getMapper(IStudentDao.class);
    }
    
    @After
    public void tearDown(){
        if(session!=null){
            session.close();
        }
    }
}

注意事项:

在mapper的动态sql中若出现大于号(>)、小于号(<)、大于等于号(>=)、小于等于号(<=)等符号,最好将其转换为实体符号。否则,xml可能会出现解析出错问题。

替换规则.PNG

1.<if/>标签

对于该标签的执行,当test的值为true时,会将其包含的sql片段拼接到其后所在的sql语句中。

例:查询出满足用户提交查询条件的所有学生。用户提交的查询条件可以包含一个姓名的模糊查询,同时还可以包含一个年龄的下限。当然,用户在提交表单时可能两个条件均作出了设定,也可能两个条件均不做设定,也可以只做其中一项设定。

查询条件不确定,查询条件依赖于用户提交的内容。此时,就可使用动态sql语句,根据用户提交内容对将要执行的sql进行拼接。

dao接口:

public interface IStudentDao {      
    List<Student> selectStudentsIf(Student student) 
}

映射文件:

<select id="selectStudentsIf" resultType="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>

测试类:

@Test
public void test01(){
    Student student = new Student();
    List<Student> students = dao.selectStudentsIf(student);
    System.out.println(students);
}

2.<where/>标签

dao接口:

public interface IStudentDao {
    
    List<Student> selectStudentsIf(Student student);
    List<Student> selectStudentsWhere(Student student);
}

映射文件:

<select id="selectStudentsWhere" resultType="Student">
    select * from student
    <where>
        <if test="name!=null and name!=''">
            and name like '%' #{name} '%'
        </if>
        <if test="age > 0">
            and age > #{age}
        </if>
    </where>
</select>

测试:

@Test
public void test02(){
    Student student = new Student();
    List<Student> students = dao.selectStudentsWhere(student);
    System.out.println(students);
}

3.<choose/>标签

该标签只可以包含<when/><otherwise/>,可以包含多个<when/>与一个<otherwise/>。他们联合使用,完成java中的开关语句switch..case功能。

需求:若姓名不空,则按姓名查询;若姓名为空,则按年龄查询;若没有查询条件,则没有查询结果。

dao接口:

public interface IStudentDao {
    
    List<Student> selectStudentsIf(Student student);
    List<Student> selectStudentsWhere(Student student);
    List<Student> selectStudentsChoose(Student student);
}

映射文件:

对于<choose/>标签,其会从第一个<when/>开始逐个向后进行条件判断。若出现<when/>中的test属性值为true的情况,则直接结束<choose/>标签,不再向后进行判断查找。若所有<when/>的test判断结果均为false,则最后会执行<otherwise/>标签。

<select id="selectStudentsChoose" resultType="Student">
    select * from student
    <where>
        <choose>
            <when test="name!=null and name!=''">
                and name like '%' #{name} '%'
            </when>
            <when test="age>0">
                and age < #{age}
            </when>
            <otherwise>
                and 1 != 1
            </otherwise>
        </choose>
    </where>
</select>

测试类:

@Test
public void test03(){
    Student student = new Student();
    List<Student> students = dao.selectStudentsChoose(student);
    System.out.println(students);
}

4.<foreach/>标签--遍历数组

<foreach/>标签用于实现对于数组于集合的遍历。对其使用,需要注意:

  • collection表示要遍历的集合类型,这里是数组,即array
  • open、close、separator为对遍历内容的SQL拼接

dao接口:

public interface IStudentDao {
    
    List<Student> selectStudentsIf(Student student);
    List<Student> selectStudentsWhere(Student student);
    List<Student> selectStudentsChoose(Student student);
    List<Student> selectStudentsForeachArray(Object[] studentIds);
}

映射文件

动态sql的判断中使用的都是OGNL表达式。OGNL表达式中的数组使用array表示,数组长度使用array.length表示。

<select id="selectStudentsForeachArray" resultType="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>
foreach标签.PNG

测试类:

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

5.<foreach/>标签--遍历泛型为基本类型的List

dao接口:

public interface IStudentDao {
    
    List<Student> selectStudentsIf(Student student);
    List<Student> selectStudentsWhere(Student student);
    List<Student> selectStudentsChoose(Student student);
    List<Student> selectStudentsForeachArray(Object[] studentIds);
    List<Student> selectStudentsForeachList(List<Integer> studentIds);
}

映射文件:

OGNL表达式中的List使用list表示,其大小使用list.size表示。

<select id="selectStudentsForeachList" resultType="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>

测试类:

@Test
public void test05(){
    List<Integer> studentIds = new ArrayList<Integer>();
    student.add(1);
    student.add(3);
    
    List<Student> students = dao.selectStudentsForeachList(studentIds);
    System.out.println(students);   
}

6.<foreach/>标签--遍历泛型为自定义类型的List

dao接口:

List<Student> selectStudentsForeachList2(List<Student> students);

映射文件:

注意,这里的当前遍历对象类型是List中的泛型,即是Student对象。

<select id="selectStudentsForeachList2" resultType="Student">
    <!-- select * from student where id in(1,3) -->
    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>

测试类:

@Test
public void test09(){
    Student student1 = new Student();
    student1.setId(1);
    Student student3 = new Student();
    student3.setId(3);
    
    List<Student> students = new ArrayList<Student>();
    students.add(student1);
    students.add(student3);
    
    students = dao.selectStudentsForeachList2(students);
    System.out.println(students);
}

<foreach/>标签执行insert操作:
在执行插入时要注意不能用外部的括号:

/**
     * 保存品牌id和类目id到中间表
     * @param bid 品牌id
     * @param cidList 所属类目id集合
     */
    void insertCategoryAndBrand(@Param("bid")Long bid, @Param("cidList") List cidList);

xml:

<insert id="insertCategoryAndBrand" parameterType="map">
      INSERT INTO tb_category_brand(category_id,brand_id)
      VALUES
      <foreach collection="cidList" item="item" separator=",">
          (#{item},#{bid})
      </foreach>
</insert>

真正执行时的sql语句:insert into tb_category_brand(category_id,brand_id) values(1000,1000),(1000,2000);
是分别每一组数据都有括号,因为不能使用open和close标签来追加括号在最外面,否则会报以下错误:

image.png

7.<sql/>标签

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

dao接口:

List<Student> selectStudentsBySQLFragment(List<Student> students);

映射文件:

使用sql片段.PNG

测试类:

@Test
public void test09(){
    Student student1 = new Student();
    student1.setId(1);
    Student student3 = new Student();
    student3.setId(3);
    
    List<Student> students = new ArrayList<Student>();
    students.add(student1);
    students.add(student3);
    
    students = dao.selectStudentsBySQLFragment(students);
    System.out.println(students);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容