如何使用Hibernate/ORM保持清洁层分离?

Seb*_*ber 15 java orm hibernate jpa java-ee

如何用Hibernate/ORM(或其他ORM ......)保持干净的层?

清洁层分离的意思是将所有Hibernate内容保存在DAO层中.

例如,在创建一个大的CSV导出流时,我们应该经常做一些像evict这样的Hibernate操作来避免OutOfMemory ...输出流的填充属于视图,但是evict属于DAO.

我的意思是我们不应该将evict操作放在前端/服务中,我们也不应该把业务逻辑放在DAO中......那么在这种情况下我们能做些什么呢?

在许多情况下,你必须做一些事情,如逐出,刷新,清除,刷新,特别是当你玩交易,大数据或类似的事情...

那么你如何使用像Hibernate这样的ORM工具来保持清晰的层分离?


编辑:在工作中我不喜欢的是我们有一个自定义抽象DAO,允许服务将Hibernate标准作为参数.这是实用的,但对我来说理论上称这个DAO的服务不应该知道一个标准.我的意思是,我们不应该以任何方式将Hibernate内容导入业务/视图逻辑.


有答案,简单还是其他?

gpe*_*che 6

如果"干净"是指上层不了解较低层的实现,通常可以应用 Tell,不要求原则.对于您的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();
        }
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以自由地处理您的特定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);
        ...
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

我会做:

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();
        ...
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

虽然有人可能会认为我正在将一些业务逻辑推到DAO层,但对我来说这似乎是一种更好的方法:将ORM实现更改为替代实现会更容易.想象一下,例如,出于性能原因,您需要Foo使用原始JDBC 来读取某些特定于供应商的扩展:使用通用DAO方法,您需要更改业务和DAO层.使用这种方法,您只需重新实现DAO层.


Gui*_*ume 5

好吧,你总是可以告诉你的DAO层做你想做的事情.拥有类似于cleanUpDatasourceCacheDAO层的方法,或类似的东西(甚至是针对不同对象的一组这些方法),对我来说并不是一种不好的做法.

然后,您的服务层就可以调用该方法,而无需考虑DAO所做的工作.使用直接JDBC调用的特定实现在该方法中不起作用.