Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you keep clean layers separation with Hibernate/ORM?

How is it possible to keep clean layers with Hibernate/ORM (or other ORMs...)?

What I mean by clean layer separation is for exemple to keep all of the Hibernate stuff in the DAO layer.

For example, when creating a big CSV export stream, we should often do some Hibernate operations like evict to avoid OutOfMemory... The filling of the outputstream belong to the view, but the evict belongs to the DAO.

What I mean is that we are not supposed to put evict operations in the frontend / service, and neither we are supposed to put business logic in the DAO... Thus what can we do in such situations?

There are many cases where you have to do some stuff like evict, flush, clear, refresh, particularly when you play a bit with transactions, large data or things like that...

So how do you do to keep clear layers separation with an ORM tool like Hibernate?


Edit: something I don't like either at work is that we have a custom abstract DAO that permits a service to give an Hibernate criterion as an argument. This is practical, but for me in theory a service that calls this DAO shouldn't be aware of a criterion. I mean, we shouldn't have in any way to import Hibernate stuff into the business / view logic.


Is there an answer, simple or otherwise?

like image 512
Sebastien Lorber Avatar asked Nov 04 '11 16:11

Sebastien Lorber


People also ask

Is Hibernate a good ORM?

Hibernate ORM enables developers to more easily write applications whose data outlives the application process. As an Object/Relational Mapping (ORM) framework, Hibernate is concerned with data persistence as it applies to relational databases (via JDBC).

Is Hibernate good for big projects?

Hibernate is a great fit for complex projects with high-performance requirements. You only need to know which features to use and which you should better avoid. During this talk, I will show you how to get the most out of your Hibernate-based persistence layer.

Is Hibernate still relevant?

No, Hibernate is not deprecated.

Why is Hibernate so popular?

It helps with intrinsic problems in interfacing otherwise incompatible databases. Java Hibernate, as an ORM tool, helps to bridge that gap between incompatible databases. Automatic SQL Query Generation - There is a common belief that developers don't need to understand how to write SQL queries.


1 Answers

If by "clean" you mean that upper layers don't know about implementations of the lower layers, you can usually apply the Tell, don't ask principle. For your CSV streaming example, it would be something like, say:

// 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();
        }
        ...
    }
}

In this way you can deal with your specific ORM with freedom. The downside of this "callback" approach: if your layers are on different JVMs then it might not be very workable (in the example you would need to be able to serialize CsvFooWriter).

About generic DAOs: I have never felt the need, most object access patterns I have found are different enough to make an specific implementation desirable. But certainly doing layer separation and forcing the business layer to create Hibernate criteria are contradictory paths. I would specify a different query method in the DAO layer for each different query, and then I would let the DAO implementation get the results in whatever way it might choose (criteria, query language, raw SQL, ...). So instead of:

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);
        ...
    }
    ...
}

I would do:

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();
        ...
    }
    ...
}

Though someone could think that I'm pushing some business logic down to the DAO layer, it seems a better approach to me: changing the ORM implementation to an alternative one would be easier this way. Imagine, for example that for performance reasons you need to read Foos using raw JDBC to access some vendor-specific extension: with the generic DAO approach you would need to change both the business and DAO layers. With this approach you would just reimplement the DAO layer.

like image 61
gpeche Avatar answered Oct 07 '22 18:10

gpeche