什么时候使用resulttype_SSM框架之MyBatis3专题2:单表的CURD操作

本文详细介绍了SSM框架中MyBatis的CRUD操作,包括插入、删除、更新、查询数据。重点讨论了如何使用resultType获取新插入记录的主键值,并通过实例演示了动态SQL的使用,如等标签,以及标签定义SQL片段以提高代码复用。此外,还讨论了如何处理多查询条件和动态SQL拼接的问题。
摘要由CSDN通过智能技术生成

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 运行结果

ef7b3655454947aead6a8ac96d6d2110.png

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注入问题

例如,有一个用于根据姓名进行查询的表单。

f029eeb6ac203148882a2349d0653b74.png

其后台SQL语句为拼接字符串:String sql = "select * from t_student where name like '%" + name + " %''";

若用户正常输入,则查询出所有“张”姓学生是没有问题的,执行的sql语句为:select * from t_student where name like '%张%';

但是,若用户有意进行SQL注入,则会输入如下内容:

addc3ecbfe896a0b7ac73c1a8f59f893.png

此时表单获取的属性值,即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缓存池中,再执行。

297fc1e24576c5fc956cca4c87c67f8b.png

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表

bfe77167ae7e751ac3806b88a6e608f9.png

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语句。

216762581dbfc8503ce35d3f9d26e093.png

2e2658193d001791d80a2ff2eb42595c.png

3.3 Dao对象的获取

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

d0f442909db051417d141e530ad8d214.png

3.4 删除Dao实现类

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

Dao实现对象是由JDK的Proxy动态代理自动生成的。

7fd257013d5c00f7c95585559f35a90f.png

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()方法的测试。

5d09530a3c8abdde7b66f26dbbfd0b3a.png

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来解决这样的问题。

60a450dfed395de9832762aafb082109.png

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

这里的条件判断使用的表达式为OGNL表达式。常用的动态SQL标签有、、、等。

有一个有意思的发现是,MyBatis的动态SQL语句,与JSTL中的语句非常相似。

4.1 测试环境的搭建

4.1.1 定义数据库表

1b2ede783f6b9670e771a4684c64580c.png

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可能会出现解析出错问题。

特别是对于小于号(

5a625f69ff94b50907ebd1c9be0a69b4.png

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

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值