Если под «чистым» вы подразумеваете, что верхние уровни не знают о реализациях нижних уровней, вы обычно можете применить
Скажи, не спрашивай принцип. Для вашего примера потоковой передачи CSV это будет что-то вроде:
// This is a "global" API (meaning it is visible to all layers). This is ok as
// it is a specification and not an implementation.
public interface FooWriter {
void write(Foo foo);
}
// DAO layer
public class FooDaoImpl {
...
public void streamBigQueryTo(FooWriter fooWriter, ...) {
...
for (Foo foo: executeQueryThatReturnsLotsOfFoos(...)) {
fooWriter.write(foo);
evict(foo);
}
}
...
}
// UI layer
public class FooUI {
...
public void dumpCsv(...) {
...
fooBusiness.streamBigQueryTo(new CsvFooWriter(request.getOutputStream()), ...);
...
}
}
// Business layer
public class FooBusinessImpl {
...
public void streamBigQueryTo(FooWriter fooWriter, ...) {
...
if (user.canQueryFoos()) {
beginTransaction();
fooDao.streamBigQueryTo(fooWriter, ...);
auditAccess(...);
endTransaction();
}
...
}
}
Таким образом, вы можете свободно обращаться с вашим конкретным ORM. Недостаток этого подхода «обратного вызова»: если ваши слои находятся на разных виртуальных машинах JVM, то это может быть не очень работоспособным (в этом примере вам потребуется возможность сериализации CsvFooWriter
).
Об общих DAO: я никогда не чувствовал необходимости, большинство шаблонов доступа к объектам, которые я нашел, достаточно различны, чтобы сделать желательной конкретную реализацию. Но, безусловно, разделение уровней и принуждение бизнес-уровня к созданию критериев Hibernate являются противоречивыми путями. Я бы указывал разные методы запросов на уровне DAO для каждого отдельного запроса, а затем позволял бы реализации DAO получать результаты любым способом, который мог бы выбрать (критерии, язык запросов, необработанный SQL, ...). Так что вместо:
public class FooDaoImpl extends AbstractDao<Foo> {
...
public Collection<Foo> getByCriteria(Criteria criteria) {
...
}
}
public class FooBusinessImpl {
...
public void doSomethingWithFoosBetween(Date from, Date to) {
...
Criteria criteria = ...;
// Build your criteria to get only foos between from and to
Collection<Foo> foos = fooDaoImpl.getByCriteria(criteria);
...
}
public void doSomethingWithActiveFoos() {
...
Criteria criteria = ...;
// Build your criteria to filter out passive foos
Collection<Foo> foos = fooDaoImpl.getByCriteria(criteria);
...
}
...
}
Я бы сделал:
public class FooDaoImpl {
...
public Collection<Foo> getFoosBetween(Date from ,Date to) {
// build and execute query according to from and to
}
public Collection<Foo> getActiveFoos() {
// build and execute query to get active foos
}
}
public class FooBusinessImpl {
...
public void doSomethingWithFoosBetween(Date from, Date to) {
...
Collection<Foo> foos = fooDaoImpl.getFoosBetween(from, to);
...
}
public void doSomethingWithActiveFoos() {
...
Collection<Foo> foos = fooDaoImpl.getActiveFoos();
...
}
...
}
Хотя кто-то может подумать, что я подталкиваю какую-то бизнес-логику к уровню DAO, мне кажется, это лучший подход: изменить реализацию ORM на альтернативную было бы проще. Представьте, например, что по соображениям производительности вам нужно прочитать Foo
s, используя необработанный JDBC, чтобы получить доступ к какому-либо конкретному расширению поставщика: при использовании общего подхода DAO вам потребуется изменить как бизнес-уровень, так и уровень DAO. При таком подходе вы просто переопределяете уровень DAO.