Spring Java 中许多 DAO 的策略
Posted
技术标签:
【中文标题】Spring Java 中许多 DAO 的策略【英文标题】:Strategy for many DAOs in Spring Java 【发布时间】:2012-11-24 20:43:31 【问题描述】:我们在现有项目中有许多 DAO(目前没有接口,但可以改变)。我们不是为每个 DAO 类连接一个 Spring 管理的 bean 并将它们注入到服务层,而是有一个类似这样的 DAO“工厂”:
public class DAOFactory
private static DAOFactory daoFac;
static
daoFac = new DAOFactory();
private DAOFactory()
public static DAOFactory getInstance()
return daoFac;
public MyDAO1 getMyDAO1()
return new MyDAO1();
public MyDAO2 getMyDAO2()
return new MyDAO2();
...
(注意 MyDAO1 和 MyDAO2 是具体的类)
这使我们能够轻松地在服务层中添加/调用 DAO 方法,而无需 1.) 将 DAO 接口作为属性添加到服务类 2.) 通过配置将 DAO 实现连接到服务方法中。 (而且我们有时在一个服务类中使用多个 DAO)。
DAOFactory.getInstance().getMyDAO1().doSomething();
到目前为止,这个策略对我们很有效(我们不需要太多切换实现),但我想知道如果我们能够重新开始,是否有更好的方法?我将 DAO 视为 bean 的自动装配,但我仍然需要在每个服务类中创建属性来表示正在使用的那些 DAO。在一个大型项目中,无论如何我都不愿意开始自动装配 bean - 我们需要为所有开发人员提供可见性。
感觉就像我在 a.) 与实现紧密耦合,但代码/配置开销较少和 b.) 与接口松散耦合,但需要大量代码/配置开销之间来回切换。
我错过了更好的方法吗?中间有什么?欢迎发表意见。
【问题讨论】:
如果你不喜欢依赖注入,为什么要使用 Spring?只需在服务类中自动装配 DAO:这就是 Sring 和 DI 的全部意义所在。然后通过注入模拟 DAO 为您的服务编写单元测试,并考虑测试它比使用静态工厂容易得多。 我们有一个与您在这里尝试的类似的类,但正如 JB Nizet 所提到的,我们将 DAO @Autowired 放入其中。自动装配 DAO 的一个关键是 Spring 默认在单例中管理它们,因此您不会创建大量对象。每次您需要使用给定的 DAO 时,您的代码示例都会创建每个 DAO 的新副本,并且应该不需要这样做。 @JB Nizet:我们大量使用 DI,只是不用于 DAO。这个方法不是我想出来的,但我有机会重做它,所以问题来了。 @Marvo:关于 new() 与单例的好点 【参考方案1】:我将把所有的 DAO 作为 Spring 管理的组件,并将它们注入到服务中以实现松散耦合。为什么你认为自动装配 bean 在大项目中不好?
只需使用 @Component 注释每个 DAO 类 并用
替换MyDao mydao = factory.getmyDao()
@Autowired
MyDao myDao;
我没有看到太多的编码/配置开销。
【讨论】:
【参考方案2】:到目前为止,我对我的项目采取了几种不同的方法,但还没有真正确定什么是“最好的”。可能没有“最佳”,但可能是“最适合您的需求”。
首先,我选择了一个基础服务类。
public abstract BaseService
@Autowired FooDAO fooDao;
@Autowired BarDAO barDao;
. . .
. . .
protected getFooDAO()
return this.fooDao;
然后在我的服务类中,我可以简单地写
Foo foo = getFooDAO().uniqueById(id);
这很有效,并且它使我的服务从 dao 实例变量的所有自动装配和访问器类中保持整洁。问题是,我现在在每个服务中都有这个基类的副本,坦率地说,嗯,这没什么大不了的。但它也会产生一种代码异味,因为它没有使用组合而不是继承,从本质上讲,DI 的一个原因是鼓励组合。
一位同事推荐了一家像您这样的工厂,并称其为 ServiceProvider。我们将其自动连接到我们的服务中。
@Component
public class ServiceProvider
@Autowired FooDAO fooDao;
public FooDAO getFooDAO()
return this.fooDao;
. . .
// yadda yadda
那么我们就有了你所拥有的东西:
Foo foo = getServiceProvider().getFooDAO().uniqueById(id);
这实在是太丑陋了,而且确实不利于清晰。因此,我们只使用了提供者的实例,并将其命名为类似 sp 的简短而甜美的名称。然后我们得到
Foo foo = this.sp.getFooDAO().uniqueById(id);
再一次,它有效。这可能是一个更好的设计。而且我们只在一个地方自动连接 DAO,而不是连接到每个服务中,尽管这两种方式都不是什么大问题。但它让我感觉更好,即使 我感觉更好 不是项目要求(但你不认为它应该是吗?)
我一直在想我们会将两者结合起来。我们将更改 BaseService 以自动装配 ServiceProvider,然后包装丑陋的调用。
public abstract BaseService
@Autowired ServiceProvider serviceProvider;
protected getFooDAO()
return this.serviceProvider.getFooDAO();
protected getBarDAO()
return this.serviceProvider.getBarDAO();
在我的服务中提供更好的速记,不需要我将每个 DAO 自动连接到每个服务中知道,这是一个完全可笑的问题。
我剩下的问题是在调试器中单步执行代码。进出每个 getWhateverDAO() 调用都很乏味,通过 getServiceProvider() 添加一个可能的步骤也无济于事。
但这就是我处理这个问题的地方。坦率地说,我想我花了很多时间来思考这个问题,因为这是避免我们的应用程序带来的所有真正难题的好方法。
【讨论】:
【参考方案3】:好问题。
我觉得很遗憾你开始使用DAOFactory
。 Spring 是一个超级灵活的工厂,所以我真的不明白你为什么需要另一个。 Spring 中的自动装配有很多优点,并且不需要接口,因此您可以轻松切换到使用 Spring 访问 DAO。恕我直言,它不会减少但会提高其他开发人员的可见性。
此外,如果您正在考虑重构 DAO 层,请查看 Google 代码中的 GenericDAO:http://code.google.com/p/hibernate-generic-dao/
我对这个库有很好的体验。它可以节省您的时间。你实际上并不需要很多 DAO。你只需要一个 DAO。您显然可以从 google 代码包装通用 DAO 并添加您的应用程序特定的术语和功能。但不要在那里添加实体特定的代码。实体特定代码应该在服务层。如果您使用 Hibernate 标准 API,则不再有脆弱的 HQL,也不会与 hibernate 耦合。这个库支持 Hibernate 和 JPA,它的 API 非常简单和强大。
【讨论】:
【参考方案4】:如果您在 1 项服务中使用过多的 DAO,您应该考虑将 1 项服务拆分为更低级别(细粒度)的服务
【讨论】:
【参考方案5】:如果您不想注释您的 DAO 类或有像 @Value
这样的配置注释污染它们,我看到两个选项:
1.使用 DAO @Bean
s 创建一个@Configuration
@Configuration
public class DaoConfiguration
@Value("$db.name")
private String dbName;
@Value("$foo.table")
private String fooTable;
@Value("$bar.table")
private String barTable;
@Bean
private FooDao fooDao()
return new FooDao(dbName, fooTable);
@Bean
private BarDao barDao()
return new BarDao(dbName, barTable);
然后为您需要的 DAO 创建一个 @Autowired
字段:
@Autowired
private FooDao fooDao;
2。创建DAO工厂@Component
如果您需要在 DAO 被销毁时进行一些清理,这很有用。
@Component
public class DaoFactory
@Value("$db.name")
private String dbName;
@Value("$foo.table")
private String fooTable;
@Value("$bar.table")
private String barTable;
private FooDao fooDao;
private BarDao barDao;
@PostConstruct
public void init()
fooDao = new FooDao(dbName, fooTable);
barDao = new BarDao(dbName, barTable);
@PreDestroy
public void destroy()
try
fooDao.close();
catch (Exception e)
log.error("Failed to clean up FooDao", e);
try
barDao.close();
catch (Exception e)
log.error("Failed to clean up BarDao", e);
public FooDao fooDao()
return fooDao;
public BarDao barDao()
return barDao;
然后在你需要 DAO 的类中为工厂创建一个 @Autowired
字段:
@Autowired
private DaoFactory daoFactory;
并将其用作:
daoFactory.barDao().findAll();
【讨论】:
以上是关于Spring Java 中许多 DAO 的策略的主要内容,如果未能解决你的问题,请参考以下文章