3.3 使用动态SQL实现更新操作

在Hibernate中,如果想要更新某一个对象,就需要发送所有的字段给持久化对象,然而在实际应用中,大多数情况下都是更新的某一个或几个字段。如果更新的每一条数据都要将其所有的属性更新一遍,那么其执行效率是非常差的。有没有办法让程序只更新需要更新的字段呢?为了解决上述情况中的问题,MyBatis提供<set>元素来完成这个工作。<set>元素主要用于更新操作,其主要作用是在动态包含的SQL语句前输出一个set关键字,并将SQL语句中最后一个多余的逗号去除。

在3.2节中学习了使用动态SQL实现多条件查询,对于查询条件多变的情况,动态SQL都可以灵活、智能地进行处理。下面学习如何使用动态SQL实现更新操作。

3.3.1 使用元素if+set改造更新操作

在修改用户信息操作中,采用封装User对象入参,根据用户id进行用户信息修改,当操作数据时,每个字段都进行了赋值更新。但是在实际项目中,用户在进行信息更新操作时,并不是所有的数据都要进行修改,对于用户没有修改的数据,数据库不需要进行相应的更新操作。即更新用户表数据时,若某个参数传入值为null时,就不需要set该字段。那么现在先测试一下之前的修改用户信息示例,观察是否能满足正常的业务需求。

修改UserMapper.java,以增加接口方法,见示例15。

img【示例15】 UserMapper.java

img

UserMapper.xml中修改用户信息的代码见示例16。

img【示例16】 UserMapper.xml

img

修改测试方法,部分代码见示例17。

img【示例17】 UserMapperTest.java

img

在上述代码中,对于更新方法(modify())的参数User对象,只设置了用户名称(userName)、用户编码(userCode)、用户密码(userPassword)、地址(address)、更新者(modifyBy)、更新时间(modifyDate)和用户id(id)这7个属性,即数据库只对6个字段(userName、address、modifyBy、modifyDate)进行相应的更新操作(注:用户id为更新的where条件)。

运行测试之后,查询更新该条数据的信息如下:

img

通过结果发现,除了设值的6个字段被更新,其他字段也均被更新了,并且更新为null。通过查看日志输出MyBatis框架的SQL语句和参数就可以很清楚地知道原因了。

通过日志的SQL语句和参数,发现未被设值的参数也进行了set操作。那么该如何解决呢?这就需要使用动态SQL的set来处理。

set元素主要用于更新操作,它的主要功能和where元素差不多,主要是在包含的语句前输出一个set,若包含的语句以逗号结束,则会自动把逗号忽略掉,再配合if元素就可以动态地更新需要修改的字段;若不需要修改字段,则可以不再被更新。下面改造UserMapper.xml中修改用户信息的语句,见示例18。

img【示例18】 UserMapper.xml

img

在上述代码中,使用set标签不仅可以动态地配置set关键字,还可剔除追加到条件末尾的任何不相关的逗号(因为在update语句中使用if标签,若后面的if元素没有被执行,则导致在语句末尾残留多余的逗号),测试代码见示例19。

img【示例19】 UserMapperTest.java

img

运行测试方法之后,控制台的日志输出如下:

img

通过观察控制台日志输出的SQL语句和参数,确认最终的运行结果正确。

经验:通过对MyBatis框架的学习,大家会发现使用MyBatis框架可以很方便地调试代码。特别是对于SQL的错误,或者执行对数据库操作之后结果跟预期不一致时,都可以在控制台找到日志输出的SQL语句及参数,放在数据库中进行执行,找出问题所在,操作直观方便。

img

在映射文件中使用元素<set>和<if>元素组合进行update语句动态SQL组装时,如果<set>元素内包含的内容都为空,则会出现SQL语法错误,所以在使用<set>元素进行字段信息更新时,要确保传入的更新字段不能都为空。

3.3.2 技能训练1

上机练习4 改造供应商表修改操作(if+set)

需求说明

改造供应商表的修改功能,使用动态SQL完善此功能。

img

(1)在ProviderMapper.xml里修改SQL语句,使用动态SQL的元素if和set。

(2)修改测试方法,并进行相应的测试。

3.3.3 使用元素if+trim改造修改操作

使用trim元素替代set元素,并实现与set元素一样的效果,接下来就修改UserMapper.xml来实现用户表的修改操作,见示例20。

img【示例20】 UserMapper.xml

img

运行测试结果正确。

对于trim元素的属性前面已详细介绍过,此处不再赘述。

经验:在实际项目中,用户的操作行为多种多样,如当用户进入修改界面而不进行任何数据的修改,但同样单击“保存”按钮,那么就不需要进行字段的更新操作吗?答案是否定的,这是由于只要用户单击“修改”按钮,进入修改页面就认为用户有进行修改操作的行为,无论是否进行了字段信息的修改,系统设计都需要进行对全部字段的更新操作。当然,实际上还有一种用户操作,即用户清空了某些字段信息,根据if标签的判断,程序不会进行相应的更新操作,这显然也是跟用户的实际需求相悖的。那么实际项目该如何操作呢?一般通过设计DAO层进行更新操作,update的set中不会出现if标签,即无论用户是否全部修改,都要更新所有字段信息(注意:前端POST请求传到后台的User对象内的所有属性都进行了设值,所以不存在测试类中出现的某些属性为null的情况)。实际运用中,if标签一般都是用在where标签中的。本书介绍set中设置if标签,目的是便于初学者进行相应的练习和加深对if的理解。

3.3.4 技能训练2

上机练习5 改造供应商表修改操作(if+trim)

需求说明

改造供应商表的修改功能,使用动态SQL完善此功能。

img

(1)在ProviderMapper.xml中修改SQL语句,使用动态SQL的元素if和trim。

(2)修改测试方法,并进行相应的测试。