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 修改映射文件
select * from t_student
注意,resultType属性并非指的是查询结果集最后的类型,而是每查出DB中的一条记录,将该记录封装成为的对象的类型。
这里resultType属性使用的是全限定性类名。对于一个映射文件来说,一般情况下是对一个类的操作均放在同一个映射文件中。所以,一个映射文件中出现的类一般是相同的。而每一个需要指定类名的地方若均需要指定全限定性类名,会比较麻烦。所以,MyBatis支持为类其别名的方式。
1.6.2 注册类的别名
在主配置文件中标签后,添加标签,指定类的别名。
1、通过标签指定:
type:全限定性类名;alias:别名。
该方式的好处是:可以指定别名为简单类名以外的其他名称。当然,弊端是,必须为每个类逐个指定,比较繁琐。
2、通过指定:
对于实体类的全限定性类名的别名指定方式,一般使用方式,这样做的好处是会将该包中所有实体类的简单类名指定为别名,写法简单方便。
但是弊端是,只能够将类的简单类名作为别名。
1.6.3 再修改映射文件
此时映射文件中就可以使用类的别名:
select * from t_student
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 修改映射文件
select * from student where id=#{jjj}
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 修改映射文件
select * from t_student where name like concat('%', #{ooo}, '%')
在进行模糊查询时,需要进行字符串的拼接。SQL中的字符串的拼接使用的是函数concat(arg1, arg2, ...)。注意不能使用Java中的字符串连接符+。
当然,也可以写为如下形式:
select * from t_student where name like "%" #{ooo} "%"
以上两种形式是等效的,都是以动态参数的形式出现在SQL语句中的。
还可以使用如下形式,只是需要注意,使用${}中的只能是value,不能使用其他。
select * from t_student where name like "%${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 修改映射文件
select * from t_student where id=#{studentId}
select * from t_student where id=#{student.id}
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中添加如下映射:
select t_id id, t_name name, t_age age, t_score score from t_student where t_id =#{###}
2.3 使用结果映射resultMap
可以使用结果映射resultMap(这里的Map是映射mapper的意思)来建立映射关系,完成由字段到属性的映射,达到将查询结果封装为对象的目的。resultMap是对resultType的增强。
直接修改映射文件mapper.xml:
select * from t_student where t_id =#{ooo}
标签中定义了由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、修改映射文件:
select * from t_student where t_name like "%" #{nameCondition} "%" and t_age > #{ageCondition}
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可能会出现解析出错问题。
特别是对于小于号(
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语句。
select * from student where 1=1
and name like "%" #{name} "%"
and age > #{age}
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语句将拼接出错。
select * from t_student
and name like "%" #{name} "%"
and age > #{age}
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,则最后会执行标签。
select * from t_student
and name like "%" #{name} "%"
and age < #{age}
and 1 != 1
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表示。
select * from t_student
where id in
#{myid}
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表示。
selct * from t_student
where id in
#{myid}
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
where id in
#{myid}
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);
}