MVC架构中的Model层是与业务逻辑和数据关联最为紧密的部分,持久层作为Model层面的主要组成,其设计的优劣势必对系统的整体表现产生至关重要的影响。 —— 《深入浅出Hibernate》
持久层是什么
持久层是在系统逻辑层面上,专注于实现数据持久化的一个相对独立的领域。说白了就是应用与数据库之间沟通的“桥梁”,将数据库代码和应用业务代码从耦合的关系中脱离出来。
持久层设计与解耦思想演进
简而言之,解耦就是将原先关联程度比较高的两个事物或者多个事物通过一些手段来达到降低其关联程度的效果。
解耦合设计要达到的目标
1.应用层解耦合--应用逻辑与数据逻辑相分离
2.资源层解耦合--逻辑结构与物理结构相分离
1.早期混乱的JDBC
早期的对数据库的操作功能是在JDBC基础上直接实现的,其中糅合着大量的JDBC连接代码和SQL代码,显得极为混乱。 例如:用户查询账户余额
public double selectAmount(int id) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
double amount = 0;
try{
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/demo", //url
"root", //user
"123456" //password
);
stmt = conn.createStatement();
String sql = "select amount from user where id = "+id+";";
rs = stmt.executeQuery(sql);
while(rs.next()){
amount = rs.getDouble(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return amount;
}
上面这段代码虽然完成了要求的功能,但SQL和业务的相互耦合会使得日后的修改变得非常困难,而且一旦数据库发生变更,对与开发者而言将会是一个噩梦。
2.XML配置文件实现参数化
相比于将数据库连接用户密码暴露在数据库操作代码中,利用XML文件配置连接将会是一个更便捷高效的方法。
举个例子,XML配置文件可以这样写
<database name="mysqldb">
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://localhost:3306/demo</url>
<user>root</user>
<password>root</password>
</database>
然后再用Java读取XML文件进行连接配置,这样就可以省去日后数据库用户名和密码变更的麻烦了,而且只需要维护一份XML文件,工作量相比之前大大减少了。
3.JDBC工具类
对之前的模式进行重新审视后,我们发现这其中有一个共同点,就是每个业务中都将涉及获取数据库连接的获取和释放,能不能将这些共同的代码抽离出来呢?
很自然地,我们就能想到用一个类封装数据库的连接和获取,对外提供统一的方法操作。
代码示例:
public class DBUtils {
public static Connection getConnection() {
Connection conn = null;
String url = "jdbc:mysql://localhost:3306/demo";
String username = "root";
String password = "123456";
try{
Class.forName("com.mysql.jdbc.Driver");
conn = (Connection) DriverManager.getConnection(url,username,password);
if(conn != null){
System.out.println("hello");
}
}catch(Exception e){
e.printStackTrace();
}
return conn;
}
public static void closeConnection(Connection conn) {
try{
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
4.DAO模式的出现
虽然XML文档配置是个不错的方法,但是我们的系统可能有成百上千的业务过程,每一个业务处理中都涉及许多重复不必要的数据库连接的获取、操作和释放过程,代码冗余,效率很低。 为了解决这个问题,我们便引入了DAO(Data Access Object)模式,通俗地说就是将对数据的增删改查操作统一封装到数据操作类中,将业务流程封装到业务逻辑类中,从而将数据访问和业务逻辑分离开来,解除两者之间的耦合关系。
DAO模式的实现层次图:
某种意义上说,DAO = Data + Accessor + Domain Object
Domain Object即是代码中常用的Java Bean对象
简单描述: 我们以用户登录为例,来讲解DAO模式从上到下的层次。
(1)业务逻辑类
只负责业务的实现过程,而无需顾及数据的具体操作,例如
public Class UserService{
public User login(String username, String password){
UserDao userDao = new UserDao();
User user = userDao.login(username,password);
return user;
}
}
(2)数据库操作类
public Class UserDao{
public User login(String username, String password){
String sql = "..."; //sql代码
Connection conn = (Connection) JDBCUtil.getConnection();
User user = null;
//... 对数据库的具体操作省略
return user;
}
public void save(){
//...
}
public void delete(){
//...
}
}
(3)Java Bean类
public class User{
private String id;
private String username;
private String password;
//... getters和setters方法
}
这种方式很好地将数据访问底层实现和业务层分离开来,意味着我们可以在上层不变的前提下,仅仅通过更换底层实现来切换数据库访问的机制,从而可以让我们的应用部署在不同的数据库平台上。更重要的是,我们可以加入数据库连接池及各种缓存机制(如Statement Cache,Data Cache),从而提升整个系统的性能。
但是,换个角度思考,我们虽然能够通过静态更换底层实现实现部署到不同的数据库平台上,但是这样的方式在面对较大的系统时,部署和维护工作将会变得非常棘手。
在面向对象设计中“可复用设计”中有一条原则,叫做“开闭原则”--对扩展开放,对修改关闭,说的通俗一点就是软件系统中包含的各种组件,例如模块、类以及功能等等,应该在不修改现有代码的基础上,引入新功能。我们应该把DAO模式中存在的变动因素屏蔽在系统之外。
由此,在DAO层引入了工厂模式(Factory)、代理模式(Proxy)。
5.工厂模式和代理模式
由于需要针对不同的数据库提供各自的DAO实现,很自然我们就联想到可以抽象出一个调用接口,通过调用接口作为调用界面和规范,从而避免对代码具体实现的依赖。
1.工厂模式
作为最常用的创建模式,工厂模式可以实现按需加载相应的实现提供给业务层。
UserDao userDao = DAOFactory.getDAO(UserDao.class);
User user = userDao.getUser(id);
工厂模式UML图:
以下简易实例中使用工厂模式实现DAO类的按需创建。
public class DAOFactory{
public static UserDao getUserDAO(){
return new UserDao();
}
public static xxxDao getxxxDAO(){
return new xxxDao();
}
//...
}
业务代码就变成了这样
public Class UserService{
public User login(String username, String password){
User user = DAOFactory.getUserDAO().login(username,password);
return user;
}
}
看起来不错,但是每次都要通过DAOFactory的方法创建DAO类,导致整段代码中充斥着重复的部分,有没有办法解决它呢?
2.代理模式
代理模式的引入正好解决了以上出现的问题。怎么回事呢?让我们先来了解一下代理模式是什么。
代理模式的UML图
一句话概括,代理模式就是在你和目标对象中间设置一个代理,你只需要告诉代理你现在需要登录,代理就会自动帮你把新建对象,操作对象等等全部搞定了。
让我们看看经过代理模式改造之后的代码:
public class UserProxy{
public static User login(String username, String password){
User user = DAOFactory.getUserDAO().login(username,password);
return user;
}
//...
}
于是业务代码就变得非常简洁,看起来比最开始的业务层代码好多了。
public Class UserService{
public User login(String username, String password){
return UserProxy.login(username,password);
}
}
结语
到现在,我们已经差不多解决了持久层的解耦合问题了,那么持久层的设计思想是不是到此就结束了呢?我们是不是已经找到了适应所有web应用的模式呢?
不是的。 无论什么应用,都无法脱离对资源的管理和使用,对于持久层而言,资源的调度和管理就显得尤为重要。 由于对数据库的操作过程中存在许多消耗资源的“重量级”操作,所以优化持久层的资源管理调度也是持久层设计理念发展过程中希望达到的目标。可见一个好的设计能够提升性能,提升系统的伸缩性、可维护性。
那么我们要怎么优化持久层呢?ORM(Object/Relational Mapper)思想乃至持久层框架的出现是为了解决什么问题呢?Hibernate、Apache OJB、iBatis是如何对持久层进行处理的呢?我们能不能一劳永逸地解决持久层存在的问题呢?
敬请期待下一篇博文,带你探秘持久层的设计理念。
感谢您的阅读!如果对您有一点点帮助的话,那将是我莫大的荣幸!
相关学习资源
XML:
设计模式: