• CURD操作,即指的是对数据库中实体对象的增Create、改Update、查Read、删Delete操作。

    1 自定义Dao接口实现类

    1.1 搭建测试环境

  • 项目crud。在之前专题1的项目中加以修改

    1.1.1 修改Dao接口

    public interface IStudentDao {
    //插入
    void insertStudent(Student student);
    void insertStudentCatchId(Student student);
    //删改
    void deleteStudentById(int id);
    void updateStudent(Student student);
    //查询所有
    List selectAllStudents();
    Map selectStudentMap();
    //查询指定学生
    Student selectStudentById(int id);
    Student selectStudentByMap(Map map);
    //根据姓名查询
    List selectStudentsByName(String name);
    }

    1.1.2 修改Dao实现类

  • Dao实现类除了已经实现的insertStudent()方法之外,其他方法暂时先以空方法体的方式实现。而具体的实现后面详细来讲。
    public class StudentDaoImpl implements IStudentDao{
    private SqlSession session;
    @Override
    public void insertStudent(Student student) {
        try {
            //使用工具类获取SqlSession对象
            session = MyBatisUtils.getSqlSession();
            session.insert("insertStu", student);
            session.commit();
        } finally {
            // 6.SqlSession关闭
            if(session != null) {
                session.close();
            }
        }   
    }
    @Override
    public void insertStudentCatchId(Student student) {
    }
    }
    //...

    1.1.3 修改测试类

  • 对于CURD的测试,使用Junit进行测试。每个方法在测试前,首先需要创建和获取Dao对象。所以,将Dao的创建放在了@Before注解的方法中。
    public class Mytest {
    private IStudentDao dao;
    @Before
    public void setUp() {
        dao = new StudentDaoImpl();
    }
    }

    1.2 单纯插入数据

    1.2.1 修改映射文件

    
        insert into t_student(name , age, score) values(#{name}, #{age}, #{score})
    
  • id:该SQL语句的唯一标识,Java代码中要使用该标识。
  • #{}:对于指定参数类型属性值的引用。其底层是通过反射机制,调用Student类相关属性的get方法来获取值的。因为底层使用的是反射,所以这里使用的是类的属性名,而非表的字段名。

    1.2.2 修改Dao实现类

  • 使用SqlSession对象的insert()方法。该方法默认返回DB中受影响的条数。其方法原型为:insert(String id, Object obj)。
    @Override
    public void insertStudent(Student student) {
        try {
            //使用工具类获取SqlSession对象
            session = MyBatisUtils.getSqlSession();
            session.insert("insertStu", student);
            session.commit();
        } finally {
            // 6.SqlSession关闭
            if(session != null) {
                session.close();
            }
        }   
    }
  • 需要注意的是,执行完成对DB的修改操作,必须要做SqlSession的提交。否则,修改将无法同步到DB中。因为使用无参的openSession()方法已经将事务的自动提交功能给关闭。

    1.2.3 修改测试类

  • 由于后面要讲解查询,所以这里要先通过插入来创建一些基本的测试数据。
    @Test
    public void testInsert() {
        for(int i = 1; i <= 10; i++) {
            Student student = new Student("n_" + i, 15 + i, 85.5 + i);
            dao.insertStudent(student);
        }
    }

    1.3 插入后用新id初始化被插入对象

    1.3.1 修改映射文件

  • MySql中在插入语句后执行如下语句,则会输出新插入记录的id。
    insert into t_student(name, age, score) values("王五", 25, 95.5);
    select last_insert_id();
    insert into t_student(name, age, score) values("王五", 25, 95.5);
    select @@identity;
  • 映射文件的标签中,有一个子标签用于获取新插入记录的主键值。以下两种写法均可以完成“使用新插入记录的主键值初始化被插入的对象”的功能。
    
        insert into t_student(name , age, score) values(#{name}, #{age}, #{score})
        
            select @@identity
        
    
    
        insert into t_student(name , age, score) values(#{name}, #{age}, #{score})
        
            select last_insert_id();
        
    
  • resultType:指出获取的主键的类型;
  • keyProperty:指出主键在Java类中对应的属性名。此处会将获取的主键值直接封装到被插入的Student对象中,即dao中insert()方法的第二个参数对象中。
  • order:指出id的生成相对于insert语句的执行是在前还是在后。MySql数据表中的id,均是先执行insert语句,而后生成id,所以需要设置为AFTER;Oracle数据库表中的id,则是在insert执行之前先生成的,所以需要设置为BEFORE。当前的MyBatis版本,不指定order属性,则会根据所用DBMS,自动选择其值。(我使用的是mybatis-3.4.6版本)

    1.3.2 修改Dao实现类

    @Override
    public void insertStudentCatchId(Student student) {
        try {
            //使用工具类获取SqlSession
            session = MyBatisUtils.getSqlSession();
            session.insert("insertStudentCatchId", student);
            session.commit();
        } finally {
            if(session != null) {
                session.close();
            }
        }
    }

    1.3.3 修改测试类

    //插入后用新id初始化被插入对象
    @Test
    public void testInsert() {
        Student student = new Student("张三", 23, 93.5);
        System.out.println("插入前student = " + student);
        dao.insertStudentCatchId(student);
        System.out.println("插入后student = " + student);           
    }

    1.3.4 运行结果

    SSM框架之MyBatis3专题2:单表的CURD操作_第1张图片

    1.3.5 id是何时获取到的?

  • 这个新插入数据的id是何时获取到的呢?是在插入操作完成后由DB回传给Dao的实现类的吗?
  • 在Dao的实现类中insert()方法后,commit()方法前插入一条输出student对象的语句,并在commit()方法处添加一个断点。
    @Override
    public void insertStudentCatchId(Student student) {
        try {
            //使用工具类获取SqlSession
            session = MyBatisUtils.getSqlSession();
            session.insert("insertStudentCatchId", student);
            System.out.println("插入操作还未提交 student = " + student);
            //此处添加断点
            session.commit();
        } finally {
            if(session != null) {
                session.close();
            }
        }
    }
  • 以调试的方式运行,发现在插入操作还未提交时student对象已经有了id。这说明一个问题:无论插入操作是提交还是回滚,DB均会为insert的记录分配id,即使发生了回滚,这个id也已经被使用。后面再插入并提交的记录数据,此id已经不能够再使用,被分配的id是跳过此id后的id。
  • 另外,从前面中order属性值的设置讲解可知,MySql在insert语句执行后会自动生成该新插入记录的主键值。主键值的生成只与insert语句是否执行有关,而与最终是否提交无关。

    1.4 删除数据

    1.4.1 修改映射文件

    
        delete from t_student where id=#{xxx}
    
  • 注意,这里的动态参数id所赋值为#{xxx},这个#{xxx}表示这就是个占位符,代表delete()方法的第二个参数。#{}中可以放任何值,无需要与delete()方法的第二个参数值相同。

    1.4.2 修改Dao实现类

    @Override
    public void deleteStudentById(int id) {
        try {
            //使用工具类获取SqlSession
            session = MyBatisUtils.getSqlSession();
            session.delete("deleteStudentById", id);
            session.commit();
        } finally {
            if(session != null) {
                session.close();
            }
        }
    }

    1.4.3 修改测试类

    //删除数据
    @Test
    public void testDeleteStudentById() {
        dao.deleteStudentById(16);       
    }

    1.5 修改数据

    1.5.1 修改映射文件

        
            update t_student set name=#{name}, age=#{age}, score=#{score} where id=#{id}
        
  • 注意,这里的#{}中,必须要填写update()方法所传第二个参数student对象的属性名称,不能够随意填写。

    1.5.2 修改Dao实现类

    @Override
    public void updateStudent(Student student) {
        try {
            //使用工具类获取SqlSession
            session = MyBatisUtils.getSqlSession();
            session.update("updateStudent", student);
            session.commit();
        } finally {
            if(session != null) {
                session.close();
            }
        }
    }

    1.5.3 修改测试类

    @Test
    public void testUpdateStudent() {
        Student student = new Student("赵六", 26, 96.5);
        student.setId(18);
        dao.updateStudent(student);
    }

    1.6 查询所有对象-返回List

    1.6.1 修改映射文件

  • 注意,resultType属性并非指的是查询结果集最后的类型,而是每查出DB中的一条记录,将该记录封装成为的对象的类型。
  • 这里resultType属性使用的是全限定性类名。对于一个映射文件来说,一般情况下是对一个类的操作均放在同一个映射文件中。所以,一个映射文件中出现的类一般是相同的。而每一个需要指定类名的地方若均需要指定全限定性类名,会比较麻烦。所以,MyBatis支持为类其别名的方式。

    1.6.2 注册类的别名

  • 在主配置文件中标签后,添加标签,指定类的别名。
  • 1、通过标签指定:
    
    
        
    
  • type:全限定性类名;alias:别名。
  • 该方式的好处是:可以指定别名为简单类名以外的其他名称。当然,弊端是,必须为每个类逐个指定,比较繁琐。
    2、通过指定:
    
    
        
    
  • 对于实体类的全限定性类名的别名指定方式,一般使用方式,这样做的好处是会将该包中所有实体类的简单类名指定为别名,写法简单方便。
  • 但是弊端是,只能够将类的简单类名作为别名。

    1.6.3 再修改映射文件

  • 此时映射文件中就可以使用类的别名:

    1.6.4 修改Dao实现类

  • 完成Dao实现类的selectAllStudents()方法,使用SqlSession的selectList()方法完成查询操作,该方法将查询出来的每条记录封装为指定类型对象后,再将最后的结果集封装为List返回。方法原型为:List selectList(String statement)。
  • statement:映射文件中配置的SQL语句的id。
    @Override
    public List selectAllStudents() {
        List students = null;
        try {
            session = MyBatisUtils.getSqlSession();
            students = session.selectList("selectAllStudents");
        } finally {
            if(session != null) {
                session.close();
            }
        }
        return students;
    }
  • 在写查询时,由于不是对DB中数据进行的修改,所以无需通过SqlSession的提交。但是最终SqlSession对象还是需要关闭的。

    1.6.5 修改测试类

    @Test
    public void testSelectAllStudents() {
        List students = dao.selectAllStudents();
        for(Student student : students) {
            System.out.println(student);
        }
    }

    1.7 查询所有对象-返回Map

    1.7.1 修改映射文件

  • 此例中映射文件不需要修改

    1.7.2 修改Dao实现类

  • 完成Dao实现类的selectStudentMap()方法。使用SqlSession的selectMap()方法完成查询操作。该查询方法会将查询出的每条记录先封装成指定对象,然后再将该对象作为value,将该对象的指定属性所对应的字段名作为key封装为一个Map对象。方法原型为:Map selectMap(String statement, String mapKey)。
  • statement:映射文件中配置的SQL语句的id;
  • mapKey:查询出的Map所要使用的key。这个key为数据表的字段名。查询出的结果是一个Map,每条记录将会对应一个Map.entry对象,该对象的key为指定字段的值,value为记录数据所封装的对象。
    @Override
    public Map selectStudentMap() {
        Map students = null;
        try {
            session = MyBatisUtils.getSqlSession();
            students = session.selectMap("selectAllStudents", "name");
        } finally {
            if(session != null) {
                session.close();
            }
        }
        return students;
    }

    1.7.3 修改测试类

    @Test
    public void testSelectStudentMap() {
        Map map = dao.selectStudentMap();
        Student student = map.get("赵六");
        System.out.println(student);
    }

    1.7.4 说明

  • 若指定的作为key的属性值在DB中并不唯一,则后面的记录值会覆盖掉前面的值。即指定可以的value值,一定是DB中该同名属性的最后一条记录值。

    1.8 查询单个对象

    1.8.1 修改映射文件

    1.8.2 修改Dao实现类

  • 使用SqlSession的selectOne()方法。其会将查询的结果记录封装为一个指定类型的对象。方法原型为:Object selectOne(String statement, Object parameter)
  • statement:映射文件中配置的SQL语句的id。
  • parameter:查询条件中动态参数的值。
    @Override
    public Student selectStudentById(int id) {
        Student student = null;
        try {
            session = MyBatisUtils.getSqlSession();
            student = session.selectOne("selectStudentById", id);
        } finally {
            if(session != null) {
                session.close();
            }
        }
        return student;
    }

    1.8.3 修改测试类

    @Test
    public void testSelectStudentById() {
        Student student = dao.selectStudentById(18);
        System.out.println(student);
    }

    1.9 模糊查询

    1.9.1 修改映射文件

  • 在进行模糊查询时,需要进行字符串的拼接。SQL中的字符串的拼接使用的是函数concat(arg1, arg2, ...)。注意不能使用Java中的字符串连接符+。
  • 当然,也可以写为如下形式:
  • 以上两种形式是等效的,都是以动态参数的形式出现在SQL语句中的。
  • 还可以使用如下形式,只是需要注意,使用${}中的只能是value,不能使用其他。
  • 这种方式是纯粹的字符串拼接,直接将参数拼接到了SQL语句中。这种方式可能会发生SQL注入。

    1.9.2 修改Dao实现类

    @Override
    public List selectStudentsByName(String name) {
        List students = null;
        try {
            session = MyBatisUtils.getSqlSession();
            students = session.selectList("selectStudentsByName", name);
        } finally {
            if(session != null) {
                session.close();
            }
        }
        return students;
    }

    1.9.3 修改测试类

    @Test
    public void testSelectStudentsByName() {
        List students = dao.selectStudentsByName("张三");
        for(Student student : students) {
            System.out.println(student);
        }
    }

    1.9.4 $和#的区别

  • $和#的区别是很大的。#为占位符,而$为字符串拼接符。
  • 字符串拼接是将参数值以硬编码的方式直接拼接到了SQL语句中,字符串拼接就会引发两个问题:SQL注入问题与没有使用预编译所导致的执行效率低下问题。
  • 一般情况下,动态参数的值是由用户输入的,则不能使用拼接符$,因为有可能会出现SQL注入;但是动态参数的值是由系统计算生成的,则可以使用拼接符$。但是这样虽然不存在SQL注入的风险,仍然存在执行效率问题。

    1.9.5 回顾:SQL注入问题

  • 例如,有一个用于根据姓名进行查询的表单。
    SSM框架之MyBatis3专题2:单表的CURD操作_第2张图片
  • 其后台SQL语句为拼接字符串:String sql = "select * from t_student where name like '%" + name + " %''";
  • 若用户正常输入,则查询出所有“张”姓学生是没有问题的,执行的sql语句为:select * from t_student where name like '%张%';
  • 但是,若用户有意进行SQL注入,则会输入如下内容:
    SSM框架之MyBatis3专题2:单表的CURD操作_第3张图片
  • 此时表单获取的属性值,即name值为:张%' or 1=1 or '。与后天的sql语句拼接在一块,则形成如下的sql:select * from t_student where name like '%张%' or 1=1 or '%'。
  • 此时SQL的执行结果为,查询出所有数据记录。
  • 若在进行查询时,使用的为PreparedStatement,而非Statement,则可防止SQL注入的产生。
  • 在MyBatis中,使用#号为占位符,则后台执行SQL使用的为PreparedStatement,将会防止SQL注入;而使用$符,则为字符串拼接,使用的是Statement,将无法防止SQL注入。

    1.9.6 回顾:SQL的预编译问题

  • 当Java代码通过JDBC的Statement向DB中发送一条SQL语句时,DBMS会对SQL语句编译后执行。
  • 当Java代码通过JDBC的PreparedStatement向DB中发送一条SQL语句时,DBMS会首先编译SQL语句,然后将编译好的SQL放入DBMS的数据库缓存池中再执行。当DBMS再次接收到该数据库操作的SQL时,先从DB缓存池中查找该语句是否被编译过,若被编译过,则直接执行,否则先编译后将编译结果放入DB缓存池中,再执行。
    SSM框架之MyBatis3专题2:单表的CURD操作_第4张图片

    1.10 根据Map进行查询

  • mapper中SQL语句的动态参数也可以是Map的key。

    1.10.1 修改测试类

    @Test
    public void testSelectStudentByMap() {
        Student student = new Student();
        student.setId(23);
    
        Map map = new HashMap();
        map.put("studentId", 18);
        map.put("student", student);
    
        student = dao.selectStudentByMap(map);
        System.out.println(student);
    
    }

    1.10.2 修改映射文件

    
    

    1.10.3 修改Dao实现类

    @Override
    public Student selectStudentByMap(Map map) {
        Student student = null;
        try {
            session = MyBatisUtils.getSqlSession();
            student = session.selectOne("selectStudentByMap2", map);
        } finally {
            if(session != null) {
                session.close();
            }
        }
        return student;
    }

    2 属性名与查询字段名不相同

  • resultType可以将查询结果直接映射为实体Bean对象的条件是,SQL查询的字段名与实体Bean的属性名一致。因为在将查询结果转换为指定类型对象时,系统自动将查询结果字段名称作为对象的属性名,通过反射机制完成对象的创建。
  • 当SQL查询结果的字段名与实体Bean的属性名不一致时,将无法创建出需要类型的对象。此时有两种解决方案。

    2.1 搭建测试环境

    2.1.1 修改student表

    SSM框架之MyBatis3专题2:单表的CURD操作_第5张图片

    2.1.2 修改Dao接口

    public interface IStudentDao {
    Student selectStudentById(int id);
    }

    2.1.3 修改Dao接口

    @Test
    public void testSelectStudentById() {    
        dao = new StudentDaoImpl();
        Student student = dao.selectStudentById(3);
        System.out.println(student);
    }

    2.1.4 定义Dao实现类

    @Override
    public Student selectStudentById(int id) {
        Student student = null;
        try {
            session = MyBatisUtils.getSqlSession();
            student = session.selectOne("selectStudentById", id);       
        } finally {
            if(session != null){
                session.close();
            }
        }
        return student;
    }

    2.2 查询字段使用别名

  • 虽然属性名称与表中字段名称不一致,但是可以为查询结果的字段名赋予别名,让别名与实体Bean的属性名相同。这样框架也就可以根据查询结果利用反射机制将对象创建。
  • 在映射文件中mapper中添加如下映射:

    2.3 使用结果映射resultMap

  • 可以使用结果映射resultMap(这里的Map是映射mapper的意思)来建立映射关系,完成由字段到属性的映射,达到将查询结果封装为对象的目的。resultMap是对resultType的增强。
  • 直接修改映射文件mapper.xml:
    
        
        
        
        
    
    
  • 标签中定义了由type指定的类的属性名到表中字段名称的映射关系。根据这个映射关系,框架利用反射机制创建相应对象。
    1、type:指定要映射的实体类;
    2、id:指定该resultMap映射关系的名称;
    3、标签:id的字段名column与实体类的属性property间的映射关系;
    4、标签:id以外其他字段名column与实体类的属性property间的映射关系;
  • 当然,对于字段名与实体类的属性名相同的情况下,可以不写入标签中。

    3 Mapper动态代理

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

    3.1 映射文件的namespace属性值

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

    3.2 Dao接口方法名

  • MyBatis框架要求,接口中的方法名,与映射文件中相应的SQL标签的id值相同。系统会自动根据方法名到相应的映射文件中查找同名的SQL映射id。
  • 简单来说,通过方法名就可以定位到映射文件mapper中的相应的SQL语句。
    SSM框架之MyBatis3专题2:单表的CURD操作_第6张图片
    SSM框架之MyBatis3专题2:单表的CURD操作

    3.3 Dao对象的获取

  • 使用时,只需要使用SqlSession中的getMapper()方法,即可获取指定接口的实现类对象。该方法的参数为指定Dao接口类的class值。
    SSM框架之MyBatis3专题2:单表的CURD操作

    3.4 删除Dao实现类

  • 由于通过调用Dao接口的方法,不仅可以从SQL映射文件中找到所要执行的SQL语句,还可以通过方法参数以及返回值,将SQL语句的动态参数传入,将查询结果返回。所以,Dao的实现工作,完全可以由MyBatis系统自动根据映射文件完成。所以,Dao的实现类就不再需要了。
  • Dao实现对象是由JDK的Proxy动态代理自动生成的。
    SSM框架之MyBatis3专题2:单表的CURD操作_第7张图片

    3.5 修改测试类

    3.5.1 @Before与@After注解方法

  • 在@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();
            }
        }
    }

    3.5.2 修改mapper映射文件

        
             insert into t_student(t_name , t_age, t_score) values(#{name}, #{age}, #{score})
        
        
                insert into t_student(t_name , t_age, t_score) values(#{name}, #{age}, #{score})
                
                        select @@identity
                
        
        
                delete from t_student where t_id=#{xxx}
        

    3.5.3 添加SqlSession的提交方法

  • 在增删改测试方法的最后,添加SqlSession的commit()方法,完成提交。
    @Test
    public void test01() {   
        for (int i = 1; i < 10; i++) {
            Student student = new Student("n_1" + i, 15 + i, 85.5 + 1);
            dao.insertStudent(student);
        }
        session.commit();
    }
    @Test
    public void test02() {   
        Student student = new Student("李四", 24, 98);
        System.out.println("student = " + student);
        dao.insertStudentCatchId(student);
        System.out.println("student = " + student);
        session.commit();
    }
    @Test
    public void test03() {
        dao.deleteStudentById(45);
        session.commit();
    }

    3.5.4 删除selectStudentMap()方法测试

  • MyBatis框架对于Dao查询的自动实现,底层只会调用selectOne()和selectList()方法。而框架选择方法的标准是测试类中用于接收返回值的对象类型。若接收类型为List,则自动选择selectList方法;否则,自动选择selectOne()方法。
  • 这里接收类型为Map,所以框架选择了selectOne()方法,会报错。所以这里需要删除这个selectStudentMap()方法的测试。
    SSM框架之MyBatis3专题2:单表的CURD操作_第8张图片

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

  • 在实际工作中,表单中所给出的查询条件有时是无法将其封装为一个对象的,也就是说,查询方法只能够携带多个参数,而不能够携带这多个参数进行封装的一个对象。对于这个问题,有两种解决方案。
  • 可以使用将这多个参数封装为一个Map方式实现。即将这多个参数封装为一个Map,根据Map进行查询。
    1、修改Dao接口:在Dao接口中添加如下方法:
    List selectStudentByMap(Map map);

    2、修改测试类:

    public void test05() {
        Map map = new HashMap(); 
        map.put("nameCondition", "李");
        map.put("ageCondition", 20);
        List students = dao.selectStudentByMap(map);
        for(Student student : students) {
            System.out.println(student);
        }
    }

    3、修改映射文件:

        
            
            
            
            
        
        

    4 动态SQL

  • 动态SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户的查询条件进行查询。提交的查询条件不同,执行的SQL语句不同。若将每种可能的情况均逐一列出,对所有条件进行排序组合,将会出现大量的SQL语句不同。此时,可使用动态SQL来解决这样的问题。
    SSM框架之MyBatis3专题2:单表的CURD操作_第9张图片
  • 动态SQL,即通过MyBatis提供的各种标签对条件作出判断以实现动态拼接SQL语句。
  • 这里的条件判断使用的表达式为OGNL表达式。常用的动态SQL标签有等。
  • 有一个有意思的发现是,MyBatis的动态SQL语句,与JSTL中的语句非常相似。

    4.1 测试环境的搭建

    4.1.1 定义数据库表

    SSM框架之MyBatis3专题2:单表的CURD操作_第10张图片

    4.1.2 定义实体

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

    4.1.3 定义测试类

  • 测试类暂时只定义@Before与@After方法,其他测试方法后面再逐步填充。

    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();
        }
    }   
    }

    4.1.4 注意事项

  • 在mapper的动态SQL中若出现大于号(>)、小于号(<)、大于等于号(>=),小于等于号(<=)等符号,最好将其转换成实体符号,否则,XML可能会出现解析出错问题。
  • 特别是对于小于号(<),在XML中是绝对不能够出现的。否则,一定会出错。
    SSM框架之MyBatis3专题2:单表的CURD操作

    4.2 < if/>标签

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

    4.2.1 定义Dao接口

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

    4.2.2 定义映射文件

  • 为了解决两个条件均未做设定的情况,在where后添加了一个“1=1”的条件。这样就不至于两个条件均未设定而出现只剩下一个where,而没有任何拼接的条件的不完整SQL语句。

    
    
    
    

    4.2.3 修改测试类

    @Test
    public void test01() {
        //Student student = new Student();
        //Student student = new Student("", 25, 0);
        //Student student = new Student("李", -22, 0);
        Student student = new Student("李", 22, 0);      
    
        List students = dao.selectStudentsIf(student);
        System.out.println(students);
    }

    4.3 < where/>标签

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

    4.3.1 修改Dao接口

    List selectStudentsWhere(Student student); 

    4.3.2 修改映射文件

  • 使用标签,在有查询条件时,可以自动添加上where子句;没有查询条件时,不会添加where子句。需要注意的是,第一个标签中的SQL片段,可以不包含and。不过,写上and也没错,系统会自动将多出来的and去掉。但是其他中SQL片段的and,必须要求写上,否则SQL语句将拼接出错。

    4.3.3 修改测试类

    public void test02() {
        Student student = new Student();
        //Student student = new Student("", 25, 0);
        //Student student = new Student("李", -22, 0);
        //Student student = new Student("李", 22, 0);        
    
        List students = dao.selectStudentsWhere(student);
        System.out.println(students);
    }

    4.4 < choose/>标签

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

    4.4.1 修改Dao接口

    List selectStudentsChoose(Student student);    

    4.4.2 修改映射文件

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

    4.4.3 修改测试类

    @Test
    public void test03() {
        //Student student = new Student();
        Student student = new Student("", 25, 0);
        //Student student = new Student("刘", 0, 0); 
    
        List students = dao.selectStudentsChoose(student);
        System.out.println(students);
    }

    4.5 标签--遍历数组

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

    4.5.1 修改Dao接口

    List selectStudentsForeachArray(Object[] studentIds);  

    4.5.2 修改映射文件

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

    4.5.3 修改测试类

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

    4.6 标签--遍历泛型为基本类型的List

  • 本例实现的需求是,查询出id为1与3的学生信息。

    4.6.1 修改Dao接口

    List selectStudentsForeachList(List studentIds);  

    4.6.2 修改映射文件

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

    4.6.3 修改测试类

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

    4.7 < sql/>标签

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

    4.7.1 修改Dao接口

    List selectStudentsBySQLFragment(List studentIds);    

    4.7.2 修改映射文件

    
    
        select * from t_student
    
        

    4.7.3 修改测试类

    @Test
    public void test06() {
        List studentIds = new ArrayList();
        studentIds.add(1);
        studentIds.add(3);
    
        List students = dao.selectStudentsBySQLFragment(studentIds);
        System.out.println(students);
    }