首页logo
  •  

jonllen

金龙,目前就职于一家软件公司,从事Java和.Net信息安全开发设计。

个人档案

jonllen
心情闪存 | 给他留言
妮称:jonllen
来自:中国. 湖南. 湘潭
简述:金龙,目前就职于一家软件公司,从事Java和.Net信息安全开发设计。
博客日历

Java工厂模式切换数据库

分类:Java

前二天,在struts介绍的实例中就提到数据库的访问使用了工厂模式,可以实现在线切换数据库的功能,同样是那个NetBeans工程,今天就来具体介绍我实现的思路。

网上有很多工厂模式的介绍,我最先接触是在.Net的PetShop项目里看到的,最近公司要搞Java,所以就搬到Java里来运用下,看了一些资料好象我这种实现的方法叫做简单工厂,是通过定义接口来实现的,在面向对象编程的世界里面,接口用来定义的一组规范,它强制规范实现类要一定要实现完成它的所有成员,至于接口的调用到底使用那个实现类则是在工厂类里面产生的。接口一般多定义对象的行为动作即方法,而抽象类则多用来定义对象的公共属性,比如男人和女人可以抽象出人做为抽象基类,因为都有人的一些公共特征,至于什么时候用接口什么时候用抽象类,这个就需要看实际项目中对象的关系了。

还是用户的增删改查功能的实现为例子,我们先需要定义用户的接口IUser,然后使用不同数据库来分别实现它,程序使用那种数据库则放在properties资源文件里配置,工厂在根据配置产生实例类,以接口形式返回到业务逻辑层,然后在页面里调用业务逻辑层方法。这里我用PowerDesiger简单画了一个类图如下:

Java工厂模式类图

首先我们来看一下IUser接口类的代码:

package dal;

import bean.User;
import java.util.List;

public interface IUser {

public int UserAdd(User user);

public int UserDel(int id);

public int UserUpdate(User user);

public User GetUserInfo(int id);

public List<User> GetUserList();
}

代码比较简单,就是定义用户业务逻辑所需要的全部方法,然后在子类里将会被一一实现。接口里增加一个方法,所有子类都必须要提供该方法的实现,不然会编译不过,这也是使用接口的一个规范问题。这里需要注意的是.Net里所有接口成员都不需要加访问修饰符,默认为public,而java则可以加public,如果接口里有字段属性则需要赋初始值,在调用的是以static final成员的形式来调用的。

我们再看一下DataAccess工厂代码:

/*
* 数据库访问工厂
*/

package dal;

/**
*
*
@author Jonllen
* @create 2009-05-18 22:04:42
* @site
http://www.jonllen.com
*/
public class DataAccess {

private DataAccess(){}

public static String daoPackageName = db.DbManager.GetConfig().getDefaultItem().getPackageName();

public static Object InstanceObject(String className)
{
String classPath
= "dao." daoPackageName "." className;
try {
System.out.println(
"--实例化" classPath "类开始--");
return Class.forName(classPath).newInstance();
}
catch (Exception ex) {
System.out.println(
"**实例化" classPath "类失败**");
ex.printStackTrace();
}
return null;
}

public static IUser CreateUser()
{
return (IUser)InstanceObject("UserDAO");
}

}

首先这个工厂类里面全是静态的成员,而且构造函数是private级别的,这其实是Java里单例的一种,为确保全局只存在一个实例。这里的daoPackageName是当前访问数据库类的包名,相当与.Net里的命名空间,我在程序里面做为一个约定,就是所有操作访问数据库的全部放在dao包下面,然后下面不同的数据库创建不同的包名,每个包下面的类名一样,这样我们根据不同数据库的包名就能创建下面类的事例,在.Net里我们使用Assembly.Load("命名空间").CreateInstance("类名全路径")程序集动态创建类的实例,而在Java里面使用Class.forName("类名全路径").newInstance(),再将Object类型强转为接口,因为这些类是实现了对应的业务接口的。在这里我推荐使用以上InstanceObject方法这种命名约定来动态创建类实例,而不要根据daoPackageName包名判断去new一个事例,如下代码是不妥的:

public static IUser CreateUser()
{
if (daoPackageName.equals("derby")) {
return (IUser)new dao.derby.UserDAO();
}
else if(daoPackageName.equals("mysql")) {
return (IUser)new dao.mysql.UserDAO();
}
else if(daoPackageName.equals("sqlserver")) {
return (IUser)new dao.sqlserver.UserDAO();
}
return null;
}

原因很简单,因为我都不知道到底将有多少像derby、mysql、sqlserver这样的实现类,所以使用daoPackageName判断去动态new对象是不合理的,这也违工厂模式设计的初衷,假如我现在新增一个oracle的实现类,那我岂不是又需要修改程序增加为oracle的判断,如果使用类全名动态实例则程序不需要修改任何源代码,只需要添加oracle实现类的引用,再设置daoPackageName为oracle即可。在实际应用中,完成好实现类,编译上传,接入到应用程序里,甚至可以不需要重启就能使用这个类。

通过使用接口,我们把功能的调用推迟给实例化的子类。不同的子类,实现的方式可能又不同,就像访问不同的数据库,sql语法可能存在差异。这里贴一下我在Java里面访问数据库的类,代码如下:

Java数据库访问辅助类

其实,不同的数据库在细节上的差异还是有很多的,比如像自动增长主键的处理等。我这个事例中用了三种数据库:derby、mysql、sqlserver,由于derby是装NetBeans自带开源免费的数据库,以前从没有听说过,所以在插入用户的时候只能先查出最大编号做为主键插入,而在mysql里插入自动编号则显示的指定为default,sqlserver里自增主键列则不需要指定,如果是oracle的话还需要使用序列。另外不同的数据库函数使用也不一,如取当前时间access和mysql是使用now(),sqlserver里则是getdate(),oracle里用SYSDATE,这些都是我们需要注意的,说不定我们的项目那一天就需要移植到另一种数据库。

java里访问数据库都是使用java.sql.*下面的类,然后加载驱动文件。而.Net里则是提供了IConnection、IDataReader、IDbDataParameter等接口,并且为不同种数据库提供了专门的命名空间,提高访问效率。下面我整理java里访问sql2000、sql2005、mysql、derby数据库的连接字符串、驱动包类名表列出比较。

数据库连接字符串驱动包
sql2000 jdbc:microsoft:sqlserver://localhost;databaseName=master;user=sa;password=123 com.microsoft.jdbc.sqlserver.SQLServerDriver
sql2005 jdbc:sqlserver://localhost\sql2005;databaseName=dbTest;user=sa;password=123 com.microsoft.sqlserver.jdbc.SQLServerDriver
mysql jdbc:mysql://localhost:3306/dbtest?user=root&password=123&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true com.mysql.jdbc.Driver
derby jdbc:derby://localhost:1527/dbTest;create=true;user=app;password=app org.apache.derby.jdbc.ClientDriver
oracle jdbc:oracle:thin:system/123:@localhost:1521:orcl oracle.jdbc.driver.OracleDriver

这些信息是配置在我工程connector.properties资源文件里的,把数据库名做为key,然后对应connString连接字符串、driverClass驱动类多个属性,使用DbManager单件类读取到,以确保只读取一次资源文件,由于要做到在线切换数据库,而不是重起应用程序后才能生效,而之前DataAccess的daoPackageName、SqlHelper的ConnString和DriverClass静态字段只会在初始化读取一次,所以切换数据库的时候也重新指定这些静态字段,不然还会是初始化时候的值,代码如下:

public void setDefaultName(String defaultName) {
for(DbItem item : this.list)
{
if (item.getDbName().equalsIgnoreCase(defaultName))
{
System.out.println(
"修改设置当前数据库为:" defaultName );

this.defaultName = defaultName;
this.defaultItem = item;

dal.DataAccess.daoPackageName
= item.getPackageName();
db.SqlHelper.ConnString
= item.getConnString();
db.SqlHelper.DriverClass
= item.getDriverClass();
}
}

}

这样切换数据库的功能就实现了,所有页面功能的调用是在BLL业务逻辑层,这一层就是使用接口实例调用方法。好了不多说了,有什么问题可以下载NetBeans工程的源代码,仍然是和上一篇struts同一工程。

Java工厂模式切换数据库实例源代码下载(带Struts的增、删、改、查功能)

标签:Java 工厂模式
  • posted@ 2009-10-23 09:33
  • update@ 2009-11-29 15:04:35
  • 阅读(13500)
  • 评论(0)

相关文章

评论
暂无任何评论。
发表评论
*必填
回复通知我
*必填

博文推荐