josh.trow
josh.trow

Reputation: 4901

Hibernate data mapping into child objects

I'm trying to figure out how to map data coming in on a request to a Hibernate object, and the issue is that the data coming in could be on the object or the child objects, and the field data is not necessarily known - the forms are user configured to contain and collect the desired data.

Roughly, the objects are like this:

Job {
  String title;

  @ManyToOne
  @JoinColumn(name = "location_id")
  JobLocation location;
}

JobLocation {
  int id;
  String description;
  double latitude;
  double longitude;
}

So if the user has defined that they want to edit the JobLocation description, we will get back in the request something along the lines of

{ jobLocationDescription: 'Santa Fe' }

How does that get mapped back to the child of the Job I'm dealing with? All I have at save time is the reference to the Job, all other items could be varying depending on what they have selected in dropdowns, etc. One option is to store a reference such as job.location.description, and have getters and use reflection to do a process-driving option:

String[] field = requestField.split(".");
Entity ent = (get object from field[0]);
if (field.length > 2) {
  ent = ent.get[get method name from next field position]();
}
ent.set[get method name from last field[] value](requestValue);

Sadly, there is nothing saying it couldn't be multiple levels, and to get the methods we are currently thinking we would have to use reflection. Are there other, better ways to do this type of operation or do we just have to slog through this?

Upvotes: 8

Views: 297

Answers (2)

Karthik R
Karthik R

Reputation: 5786

I had almost similar type of requirement in our project. We ended up using the reflection + annotation for mapping. On a nutshell, we were having something like this to construct the object.

class Job {
  String title;

  @ManyToOne
  @JoinColumn(name = "location_id")
  @EntityMapper(isRef="true")//Custom Annotation
  JobLocation location;
}

class JobLocation {
  @EntityMapper(fieldName="jobLocationId")
  int id;
  @EntityMapper(fieldName="jobLocationDescription")//Custom Annotation
  String description;
  double latitude;
  double longitude;
}

If you haven't got what I mean, we created custom annotation and we wrote a utility method that via reflection loops through the elements that has annotations as follows:

for (Field field : object.getClass().getDeclaredFields()) {
   //check for the EntityMapper annotation
   if (field.getAnnotation(EntityMapper.class) != null) {
       .
       .
       .//Use more reflection to use getters and setters to create and assign values from the JSON request.
   }
}

Upvotes: 5

Steve McKay
Steve McKay

Reputation: 2223

If you know at compile time the name of the method you're calling, you don't have to use reflection. It sounds like you need reflection. However, that doesn't mean you have to use the raw java.lang.reflect API, which is pretty painful to use directly. If you're already using Spring, BeanWrapperImpl is a very nice utility that can use your existing ConversionService to convert your input to the target type. Otherwise I'd suggest Commons BeanUtils, which does pretty much the same thing without the Springiness. Both libraries know how to handle nested properties and Map-valued properties as well.

Upvotes: 0

Related Questions