- 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 运行结果
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
- 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注入问题
- 例如,有一个用于根据姓名进行查询的表单。
- 其后台SQL语句为拼接字符串:String sql = "select * from t_student where name like '%" + name + " %''";
- 若用户正常输入,则查询出所有“张”姓学生是没有问题的,执行的sql语句为:select * from t_student where name like '%张%';
- 但是,若用户有意进行SQL注入,则会输入如下内容:
- 此时表单获取的属性值,即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缓存池中,再执行。
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表
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语句。
3.3 Dao对象的获取
- 使用时,只需要使用SqlSession中的getMapper()方法,即可获取指定接口的实现类对象。该方法的参数为指定Dao接口类的class值。
3.4 删除Dao实现类
- 由于通过调用Dao接口的方法,不仅可以从SQL映射文件中找到所要执行的SQL语句,还可以通过方法参数以及返回值,将SQL语句的动态参数传入,将查询结果返回。所以,Dao的实现工作,完全可以由MyBatis系统自动根据映射文件完成。所以,Dao的实现类就不再需要了。
- Dao实现对象是由JDK的Proxy动态代理自动生成的。
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()方法的测试。
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来解决这样的问题。
- 动态SQL,即通过MyBatis提供的各种标签对条件作出判断以实现动态拼接SQL语句。
- 这里的条件判断使用的表达式为OGNL表达式。常用的动态SQL标签有
、 、 、 等。 -
有一个有意思的发现是,MyBatis的动态SQL语句,与JSTL中的语句非常相似。
4.1 测试环境的搭建
4.1.1 定义数据库表
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中是绝对不能够出现的。否则,一定会出错。
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); }