MyBatis系列九 实用的场景(下)

六、上传文件到服务器

 

  在当今移动互联网时代使用上传头像、资料等功能是相当频繁的,本场景主要介绍 Spring MVC下上传文件的实例。一般而言,我们上传的文件都不会保存到数据库中而是保 存在文件服务器上,而把文件的路径保存到数据库中,需要我们注意一些细节,以提高系 统性能。

 

  这里我们先看一个糟糕的文件上传实例,读者请思考一下为什么它是糟糕的。我们创 建页面,这里使用的是easyui,其实使用普通的HTML也可以看到效果,如代码清单9.41 所示。

 

 

 

   现在我们搭建Spring MVC控制器,它主要提供访问。当我们的请求提交的时候,Spring MVC控制器会根据RequestMapping的配置跳转到这个控制器上,如代码清单9-42所示。

 

 

 

   完成上面的操作后就要上传具体的服装类了。这里会用到的服务类有Service接口和 Service实现类,它们主要提供上传和记录数据库信息的操作,如代码清单9.43所示。

 

 

 

   我们看看这个insertFile方法,我们先插入数据到数据库,然后进行写入文件到服务器 的操作,貌似一切都很正常,逻辑也没有问题,但是笔者必须告诉你这是一段糟糕的代码。 为什么呢?请思考一下。

  在互联网时代,尤其是高性能的网站系统一个最重要的资源是数据库连接资源。如果 不能准确关闭它们,那么系统将是一个低性能的系统。如果你对Spring 了解,•就应该明白 Spring的数据库事务存活在Service层,从第一条SQL的执行就打开了数据库连接资源, 直至方法结束。这里我们插入了一条数据到数据库,但是在我们将文件写入服务器的过程 中,Spring并没有关闭数据库资源,直至上传成功,方法结束,Spring才会关闭数据库资 源,试想如果有多个用户在上传大型文件,或者在高并发的环境中,多个上传文件的服务 同时进行,就会占用大量的数据库连接,这时就极其容易宕机。这就是一个典型的没有正 确关闭数据库连接资源引发系统的瓶颈问题,下面我们用示意图来描述它,这样会更加直 观一些,如图9.1所示。

  为了避免这些文件上传的代码应该考虑剥离文件上传的代码到Controller中去,让我们 看看重写Controller上传文件的方法,如代码清单9-44所示。  

 

 

 

   这里我们先上传了文件,然后调度insertFile方法,显然数据库的事务打开时间和文件 上传服务器没有交集,避免了数据库事务长期得不到释放的可能性,这样就大大降低了宕 机的可能性,提高了系统性能,如图9.2所示。

  除了文件操作,还有其他的一些与数据库无关的操作也是值得我们注意的,在一些没有必要使用到数据库资源的操作上,应该尽量避免数据库连接资源被占用。

 

 七、在映射中使用枚举

 

  在很多时候,我们希望将数据库的某些字典项转化为我们Java的枚举对象,这些在 MyBatis typeHandler 中可以实现。而在 MyBatis ,EnumTypeHandler EnumOrdinalType Handler并不是很好用。这个时候我们需要的是一个自定义的typeHandler去处理它们。现 在让我们举个例子。

 

  现在我们在系统里面定义了红、黄、蓝三种颜色可以使用。于是我们可以得到一个颜 色枚举类,如代码清单9.45所示。

 

   那么我们还需要定义一个自定义的typeHandler就可以了,参见代码清单9-46。

 

   我们在映射器中配置JavaType和JdbcType以及typeHandler便可以使用自定义的类型T,如代码清单9.47所示。

 

 

 

 注意加粗的代码的配置,这样就可以在这里使用映射为枚举,而不需要在配置文件里 面添加任何的配置了。

八、多对多级联

  我们在第4章里面讨论了一对一、一对多的级联以及鉴别器的使用,在现实中还会有 多对多的级联。比如用户和角色,一个用户可以拥有多个角色,同样一个角色也可以拥有 多个用户。这样就是多对多的关系,有可能我需要查询的一个用户有多少个角色,也有可 能查询一个角色下有哪些用户。一般而言,处理多对多的关系釆用双向关联的形式。这就 意味着你中有我,我中有你。我们一般的做法是把它们拆分为两部分,使角色类和用户类 形成一对多的格局,同时用户和角色也是一对多的格局,通过这两个一对多来实现多对多 的功能。在很多的情况下,我们有时候只需要用户的信息而不需要角色的信息,所以这个 时候我们设置的策略为角色可以延迟加载。同样,对角色而言,用户也需要延迟加载,以 保证没有必要的性能丢失。

  关于这个模型请参看附录A部分,对数据库模型的描述。

  首先,我们需要构建POJO,这里我们在角色类Role)里面需要构建一个List对象, 它的泛型为用户类;而在用户类中我们也需要一个List属性,其泛型为角色类,于是我们 得到了如代码清单9-48所示的两个类。

 

 

 

   这样,我们取出角色的时候,关联的用户就可以保存在其属性userList中;同样,我 们取出用户的时候,其角色也可以保存在属性roleList中。这里我们拆解为两个一对多的关 联,所以在关联的时候需要用到的是resultMap中的collection元素,我们需要在映射文件 中配置关联关系,方法如代码清单9-49所示。

 

 

 

 

 

   这个配置和平时的配置大致是一样的,只是在加粗的代码上做了一些处理,它们都用 select元素,用全限制名的方式指向了对应的SQL,这样MyBatis就知道用对应的SQL 把数据取回来,然后保存到你的POJO中。这里我们用了 column,它是制定传递的参数,而我们把fetchType设置为lazy,这样配置是为了达到延迟加载的作用。当我们取出角色而不访问其关联用户的时候,MyBatis便不会发送对应的SQL取回我们并不关心的数据,只有当我们访问其关联的数据的时候,它才会发送SQL取回我们感兴趣的数据。让我们运行 一下代码清单9-50。

 

   运行此代码,我们可以得到下面的结果。

 

 

 

 我们打印出来的SQL 一共是三句,其中通过用户访问角色的SQL并没有打印出来, 因为我们没有通过用户去访问角色,只有我们访问了角色才会运行,这就是延迟加载。

九、总结

 

 

本章主要是带领大家去编写一些实用场景的代码,有一些是我们之前谈到过的场景, 大家可以当作一次很好的复习,比如分表,使用插件进行分页,使用typeHandler处理枚举 类型,使用关联关系来处理多对多关联等,这是一些常用的场景。

还有一些常用但不容易处理好的场景,比如上传文件的事务处理,批量更新,这些场 景容易出现的问题需要读者自己用代码去编写,在实际操作中才能得到深刻体会和理解。

此外,我们之前没有提及的内容,比如存储过程的in和。ut参数,游标的处理,以及 BLOB字段的读写等,这些需要我们认真学习,小心处理,尤其是BLOB字段极其容易产 生性能问题。

以上是一些在互联网中常见的场景,笔者给出了自己的看法,在编写的过程中我们都 可以感受到MyBatis的运行过程和一些编程的技巧,有助于大家对MyBatis框架的理解, 在工作和学习中需要注意它们的使用,以避免不必要的错误和性能丢失,造成系统的瓶颈。

 

posted @ 2020-09-08 14:37  跃小云  阅读(90)  评论(0编辑  收藏  举报