4

I have been following an excellent guide for generating pojos from a mysql database using hibernate. One can find the guide here for reference: Generate pojos with hibernate

I am getting pojos which have fields that embed other objects when a foreign key was present. For example, user's have addresses. Hibernate is generating something like the following:

public class User(){
 private String name;
 private Integer uid;
 private Address address;
}

I have a problem, though, in that I want the classes to actually contain the foreign key value. For example, I want the User object to have a class field corresponding to the database field for addressId. So, I want the object to actually look something like this:

public class User(){
 private String name;
 private Integer uid;
 private Integer addressId;
 private Address address;
}

Does anyone know how to modify the hibernate code generation process so as to include foreign key values as fields on the object?

Update: I found a SO post which describes how to ignore the foreign key relationships and just get foreign keys as class fields: How to ignore foreign keys?

The problem here is that I want both. I don't want to ignore the relationships. I want them represented, but I also want the actual foreign key values.

Update:

Let me be more specific as to why I want this solution. We are trying to serialize these hibernate objects. Now, we have a lot of different hibernate pojos which are being reverse engineered. We do not want to manually write a serialization routine for every class. We would have to do that if we followed the convention of "just manually write an access method to the foreign key field on the embedded object". Further, even if we were to do so, the pojo still doesn't know what the field of the foreign key is called. Instead, we are using gson with a type adaptor.

With gson, we are serializing all fields on the pojo and just ignoring fields that contain a hibernate object. The problem, of course, is that we don't have the foreign key fields. We need a few pieces of information here in order to generically serialize any hibernate pojo. We need to know:

  1. The foreign key field name
  2. The foreign key field value
Community
  • 1
  • 1
melchoir55
  • 6,842
  • 7
  • 60
  • 106

3 Answers3

2

Your approach violates Hibernate convention. Because Hibernate uses reflection, convention is essential for Hibernate to do it's job. Because of this, I suspect Maouven's "follow the convention" approach is easiest. However, if it is non-negotiable, you have two options available.

Your first option is to add a transient getter, to expose the getAddressId() function.

public class User() {
    private String name;
    private Integer uid;
    private Address address;

    // Getters, setters...

    @Transient
    public boolean getAddressId() {
        address.getId();
    }
}

Your second option is to add a Data Access layer to impose your own conventions on top of Hibernate objects. This layer of abstraction will not be bound by Hibernate's conventions. This way, your POJOs will be wrapped by DAOs (Data Access Objects), which you can design as you see fit.

Update:

Given your unique case, consider modifying your serialization step. GSON normally can't use transient methods, but there is an extension that can do this, as shown here.

Another solution would be to use reflection to copy the object the way you want it, and then use GSON to serialize the copied object.

Community
  • 1
  • 1
stephenwebber
  • 633
  • 5
  • 16
  • Let's say we used the getAddressId() method. The system still doesn't know what the field name of the foreign key for serialization purposes. I wrote an update which explains our specific use case just to help with context. It is surprising to me that a huge percentage of the hibernate community hasn't had this exact problem. Surely people don't sit down and manually write/maintain dozens if not hundreds of serialization routines? – melchoir55 Dec 24 '15 at 04:38
  • Regarding the DAO wrappers, how would I go about getting the foreign key field names and values into such an object? There is a similar problem here, it is just up one layer. – melchoir55 Dec 24 '15 at 04:40
  • Thank you for the update. Personally, we do have separate POJOs that get serialized, and we have copy constructors that fill them. This avoids the problem where two opinionated libraries try to use reflection on the same object. – stephenwebber Dec 24 '15 at 16:03
  • DAO wrappers avoid the problem in a similar way. The extra layer allows each opinionated library to use reflection the way it wants to. This again isn't automated, so it's probably not a good solution for you. – stephenwebber Dec 24 '15 at 16:18
  • Regarding your update, wouldn't this require writing a custom serializer for every class then registering it with gson? If so, this wouldn't really be more maintainable. – melchoir55 Dec 25 '15 at 00:25
  • The GSON extension, that serializes methods as fields, would not require a custom serializer for every class. Every class would require a @Transient method on the POJO, but no more. This assumes you can use gson-fire in your application. – stephenwebber Dec 26 '15 at 05:00
  • Using gson-fire isn't a problem. Is there a way to programatically create these transient methods? The problem of course is that one needs to know the name of the foreign key fields. Is there maybe a way to modify the reveng process of hibernate to add these methods? – melchoir55 Dec 27 '15 at 04:24
  • Awarding bounty because it is going to expire,but if we could add an update to your explanation that explains how to modify the reveng process to add these transient methods then the question would be truly answered fully. – melchoir55 Dec 28 '15 at 00:09
  • Are there any consistent naming patterns? A complicated find/replace (to insert the new statement) would be the easiest, assuming all of the ids are named "id" or "classnameId." – stephenwebber Dec 28 '15 at 18:08
  • The fields do not have a consistent naming pattern, I'm afraid. Is there a place where this sort of post-processing can be defined in hibernate? – melchoir55 Dec 28 '15 at 18:45
  • You can modify the reverse engineering process if you like. Here's an example of a custom process that renames the primary keys to "id" (which will create a consistent naming pattern). This seems like a good place to start. https://docs.jboss.org/tools/latest/en/hibernatetools/html/reverseengineering.html#custom-reveng-strategy – stephenwebber Dec 28 '15 at 20:39
  • Do you know if anywhere in this process is aware of the names for the foreign key fields? These would be required in order to programatically generate these methods. – melchoir55 Dec 28 '15 at 22:05
0

Your approach will cause redundancy in the class's data. In the first bunch of code -generated by Hibernate-, you can get the addressId you need from the Address property of the User Class :

yourUser.getAddress().getAddressId();
stephenwebber
  • 633
  • 5
  • 16
Maouven
  • 330
  • 5
  • 11
  • I do realize that the same information is encoded twice, once as the foreign key and once as the unique id. I find the convenience of having the data on the object is worth the tradeoff of the very small memory footprint. Having the foreign keys explicitly mentioned makes writing generalized methods much easier. – melchoir55 Dec 16 '15 at 08:23
0

Two possible (theoretically speaking) solutions, but require manual refactoring after reverse engineering by Hibernate Tools:

I'm using annotations just for brevity

1) expose the column with mapping:

@Entity
class User
{
    @Id
    @Column
    private Integer uid;

    @Column
    private String name;

    @Column(name = "ADDRESS_ID", insertable = false, updatable = false)
    private Integer addressId;

    @ManyToOne
    @JoinColumn(name = "ADDRESS_ID")
    private Address address;
}

2) use @Transient + @PostLoad:

@Entity
class User
{
    @Id
    @Column
    private Integer uid;

    @Column
    private String name;

    @Transient
    private Integer addressId;

    @ManyToOne
    @JoinColumn(name = "ADDRESS_ID")
    private Address address;

    @PostLoad
    public void postLoad()
    {
        addressId = Optional.ofNullable(address).map(Address::getId).orElse(null);
    }
}

Another solution could be possible using JSON marshaller for JAXB:

@XmlRootElement
class User
{
    @XmlID
    private Integer uid;

    private String name;

    @XmlIDREF
    private Address address;
}

You can find a quickstart here

Michele Mariotti
  • 7,372
  • 5
  • 41
  • 73