假设我有一个存储库(一种用于访问数据的对象)接口。
我为这个问题编了一个简单的答案。
interface UserRepository {
void insert(final User user);
void update(final User user);
List<User> findAll();
}
现在,由于我管理不同类型的DBMS,这个存储库是通过以下方式实现的:
class MySqlUserRepository implements UserRepository { ... }
class OracleUserRepository implements UserRepository { ... }
class SqlServerUserRepository implements UserRepository { ... }
由于大多数SQL语句都可以在这些实现之间共享,所以我要做的第一件事是添加一个抽象基类,它提供了所有基本实现。
abstract class AbstractUserRepository implements UserRepository {
// Provide access to statements
private final Configuration configuration;
// Provides access to JDBC operations
private final NamedParameterJdbcTemplate jdbcTemplate;
AbstractUserRepository(
final Configuration configuration,
final NamedParameterJdbcTemplate jdbcTemplate) {
this.configuration = configuration;
this.jdbcTemplate = jdbcTemplate;
}
... Implementations
}
所以现在所有的实现
extends AbstractUserRepository
.
但是,我必须重写
OracleUserRepository
要使用不同的语句。。。
class OracleUserRepository extends AbstractUserRepository {
private final Configuration configuration;
private final NamedParameterJdbcTemplate jdbcTemplate;
OracleUserRepository(
final Configuration configuration,
final NamedParameterJdbcTemplate jdbcTemplate) {
super(configuration, jdbcTemplate);
this.configuration = configuration.subset("user.sql.oracle");
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void update(final User user) {
// Use Configuration and JdbcTemplate
}
...
}
... 还有一些方法
SqlServerUserRepository
显然,使用同样的方法。
现在,你可以看到我们已经复制了类属性,也许方法基本上是一样的:
AbstractUserRepository#update
:
void update(final User user) {
final var parameters =
new MapSqlParameterSource()
.addValue("field1", user.field1)
.addValue("field2", user.field2);
jdbcTemplate.update(configuration.getString("user.sql.update"), parameters);
}
OracleUserRepository#update
:
void update(final User user) {
final var parameters =
new MapSqlParameterSource()
.addValue("field1", user.field1)
.addValue("field2", user.field2);
// Note the subtle difference. We got a "subset" (user.sql.oracle) in the constructor
jdbcTemplate.update(configuration.getString("update"), parameters);
}
我觉得这不对,已经有太多重复了。你不也这么想吗?
另一种方法是使用授权。这意味着拥有一个分离的对象,它不会继承
UserRepository
接口,它的方法也接受SQL语句,而不仅仅是对象。
class UserRepositoryDelegate {
private final NamedParameterJdbcTemplate jdbcTemplate,
UserRepositoryDelegate(final NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void update(
final String statement,
final User user) {
final var parameters =
new MapSqlParameterSource()
.addValue("field1", user.field1)
.addValue("field2", user.field2);
jdbcTemplate.update(statement, parameters);
}
...
}
此对象将被注入其他存储库:
class OracleUserRepository implements UserRepository {
private final Configuration configuration;
private final UserRepositoryDelegate delegate;
OracleUserRepository(
final Configuration configuration,
final UserRepositoryDelegate delegate) {
this.configuration = configuration.subset("user.sql.oracle");
this.delegate = delegate;
}
@Override
public void update(final User user) {
delegate.update(configuration.get("update"), user);
}
...
}
还有
AbstractUserRepository
可能会变成混凝土
GenericUserRepository
(请注意,我没有获得特定DBMS的属性子集,而是使用通用属性):
class GenericUserRepository implements UserRepository {
private final Configuration configuration;
private final UserRepositoryDelegate delegate;
GenericUserRepository(
final Configuration configuration,
final UserRepositoryDelegate delegate) {
this.configuration = configuration;
this.delegate = delegate;
}
@Override
public void update(final User user) {
delegate.update(configuration.get("user.sql.update"), user);
}
...
}
这个
GenericUserRepository
也可用于其他实现:
class MySqlUserRepository implements UserRepository {
private final GenericUserRepository genericUserRepository;
MySqlUserRepository(final GenericUserRepository genericUserRepository) {
this.genericUserRepository = genericUserRepository;
}
@Override
public void update(final User user) {
genericUserRepository.update(user);
}
...
}
对于这一具体案例,代表方法是更好的策略吗?
你会用另一种方式解决这个“问题”吗?
编辑:为了让您了解更多,唯一正确的存储库实现是在启动时选择,并注入一个单一的、具体的,
UserService
.