1

So I am working now on a RESTful API that acts as a layer between the client and our databases, I'm currently using Hibernate for ORM and I've been trying for days now to find a solution to my problem, searching everywhere to no avail.

I can't post the actual code I'm using because its too big, but picture this:

I have a class Employee mapped as an Hibernate entity that looks like this

@Entity
public class Employee {
    @Id
    Integer id;
    String name;
    Double salary;
    String department;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "company_id")
    Company company;

//    Getters/Setters ommitted


    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                ", department='" + department + '\'' +
                ", company=" + company +
                '}';
    }
}

And another entity class Company that looks like this:

@Entity
public class Company {

    @Id
    Integer id;
    String name;
    String address;
    @OneToMany(mappedBy = "company", fetch = FetchType.LAZY)
    List<Employee> employees;

//    Getters and setters ommited


    @Override
    public String toString() {
        return "Company{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", employees=" + employees +
                '}';
    }
}

The database looks something like this:

Employee:
| id | name     | salary | department | company_id |
|----|----------|--------|------------|------------|
| 1  | john doe | 3000   | Finance    | 1          |
| 2  | mary sue | 3300   | HR         | 1          |

Company:
| id | name | address    |
|----|------|------------|
| 1  | ACME | St. Sesame |

Now lets say I want to get data from a certain employee, I want only the employee data + the ID of the company (and only the ID) as a reference if I need the company data later. It would look something like this:

Session session = SessionManager.openSession();

Employee emp = session.get(Employee.class, 1);

session.close();

Now I want to show the employee data

System.out.println(emp);

The output that is expected (or at least, that I expected) here is something like this:

Employee{id=1,name='john doe',salary=3000.0,department='Finance',company=Company{id=1,name=null,address=null,employees=null}}

That's simple enough, the problem is that it will throw an LazyInitializationException, because Hibernate tried to use a proxy object to fetch de Company data when it was required on the toString() call of Employee, but the session is already closed.

The thing is if I closed the session without fetching any more data, then THATS IT, no more data needed, HIBERNATE! If I open a session, I will fetch all the data that I need, and ONLY the data that I need, and after I close it, it means that I dont need any more data, thank you hibernate, but Hibernate's proxy objects keep active even after the session is closed.

I've searched all around the net, even here on StackOverflow, and there are some so called "solutions" or "workarounds", like:

  • Why don't you use HQL with constructors?

Well I actually use those, on many use cases, when the query is simple enough to use HQL. The problem is many use cases require complex querys, with lots of joins and calculations that even use tables that are not mapped to Hibernate Entities, so I need an Native Query to do so.

  • You could use HQL with constructors, and create entities for every database table you need to access on the query

That would mean mapping dozens of entities that I would use on one, maybe two use cases, and never again, just so I could use HQL with Constructors. Besides, some of those queries use native functions and other complex calculations HQL simply does not support.

  • Then use an Native Query, and you can put NULL as columnName when you dont want that column data

I do that too on some use cases, the problem is when that column is a join column on an entity relationship, Hibernate sets a proxy object, and I don't want an proxy object, just the data of the join column if, AND ONLY IF, that data is needed on another part of the code, where I can open a new session and retrieve the required data.

  • Why don't you just keep the session open using that open session on view pattern?

No can't do, thats a RESTful API, what if the client asks for the employee data on a first request, and then later makes another request for the company data of that previous employee, passing the company ID on the body of the request? Thats a common use case.

  • session.detach()?

Doesn't work.

  • Stateless sessions?

Still creates proxy objects, it only disables first and second level caches for the current session.

  • Why don't you use DTOs?

And create a new class for each use-case of partial data request of my API? HAHAHA!

You see, the only reason I use an ORM solution like Hibernate is because I dread manual handling of ResultSets do DEATH. Forget about HQL, Criteria API, etc. I don't mind creating native queries from Strings if I can only have Hibernate to map the ResultSet to an Object (Or list of objects) for me. And I want Hibernate to know that if there is an entity relation that I did not fetch before the session is closed, that means that I do NOT want to fetch it, so no proxy objects.

I know that there probably is another ORM solution out there that does not have this issue, but I can't give the time to learn it because DEADLINES, so someone help me please.

EDIT

So some of you marked my question as a duplicate of Converting Hibernate proxy to a real entity object, but there are differences:

  • That question revolves around how to "Unproxy" an object, as in how to trigger the initialization of an Hibernate proxy.

  • My question is about how to make Hibernate NOT use a proxy for an unfetched relation object, and instead just use an instance of the relation's class with only the ID of it.

In my previous example, instead of using a proxy to lazy fetch the Company object related to the Employee later when needed, I would like for hibernate to just create a new Company(company_id) and leave it like that, NO PROXY.

EDIT 2

I just found out that the Jackson project (which I use for reading/writing data as JSON in my API) has a module that ignores Hibernate proxies when serializing objects.

Maven dependency:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>${jackson-version}</version>
</dependency>

Then just register the module on you Object Mapper instance:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate5Module());
  • Possible duplicate of [Converting Hibernate proxy to a real entity object](https://stackoverflow.com/questions/2216547/converting-hibernate-proxy-to-a-real-entity-object) – XtremeBaumer May 28 '19 at 14:28

1 Answers1

0

So it seems my EDIT 2 turned out to be a "kind of" solution to my problem.

The jackson-datatype-hibernate5 initially ignores unfetched proxies, setting them as null when serializing objects, but the class Hibernate5Module (or whatever Hibernate module version you want to use) has an really handy configuration that makes Jackson serialize only the ID for unfetched proxies, which is what I was looking for.

// Configuring `ObjectMapper` and module:
ObjectMapper mapper = new ObjectMapper();
Hibernate5Module module = new Hibernate5Module()
    .configure(Hibernate5Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);

mapper.registerModule(module);

This way you can use that ObjectMapper to create ObjectReaders for your classes that will NOT try to initialize proxy objects on serialization, but only serialize the ID of the object (if its a OneToOne or ManyToOne relation, of course).

And I said its a "kind of" solution because Hibernate is still using proxy objects for unfetched relations (when I wanted just the ID value of the proxy object for later use), the only thing is that I have configured Jackson to persist the ID value and ignore the proxy object.