Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate how to implement dynamic data structure

I have this massive data structure - I would like to avoid specifying class for each type...
Using hibernate, can this structure be implemented in such way that when adding new species or foreign key - no recompilation will be needed?

Animal
|
    Birds
    |   Parakeet 
        |   Love Bird  -> [one to many:visit record]
        |   Budgerigar -> [one to many:visit record]
    Mammals
    |   Dog -> [one to many:vaccinations] [one to many:visit record] [one to many:Haircuts] 
    |   Cat -> [one to many:vaccinations] [one to many:visit record]
    |   Horse -> [one to many:vaccinations] [one to many:Horse breeding]


Tree is about 100+ types of animals

So for example -

Example1:I can add additional type without recompiling the code

     | Cow -> [one to many:vaccinations] -> [one to many:pregnancy dates]

Example2: ability to Create dynamic links between entities

    |   Horse -> [one to many:vaccinations] [one to many:Horse breeding]
            LinkToOwner->   [one to one: owner]
like image 969
JavaSheriff Avatar asked Dec 06 '13 03:12

JavaSheriff


2 Answers

Hibernate Dynamic mappings are the recommended solution for this use case. With dynamic Hibernate mappings everything gets mapped into Java Maps where keys are strings and values are types such as String, Integer, etc. or other maps.

So in the case of your example you would get a map with two entries with keys "Birds" and "Mammals" that themselves would be maps.

Mammals value would be a map with 3 entries "Dog", "Cat" and "Horse". The "Cat" value would be a map with two entries: "vaccinations" and "visit record", etc.

Dynamic mappings can be used together with the more frequently used static mappings.

The dynamic mappings are not the most frequently used feature of Hibernate, but they are stable since a long time and they are made preciselly for this use case.

like image 91
Angular University Avatar answered Nov 15 '22 08:11

Angular University


In my opinion hibernate isn't really designed for such scenario. Hibernate requires a pre-defined java class types to map to on compile-time so it knows how to generate SQL.

You can however try writing a JDBC ResultSet data access class without hibernate that allows schema changes without requiring java compilation by utilizing ResultSet getMetadata() method and storing all the columns in a Map. The key of the map is the SQL column name:

public class Animal {
  private Map<String,Object> properties = new HashMap<String,Object>();

  // getters & setters..
}

One problem when using this approach is again you don't know which of the jdbc ResultSet get method you should use: rs.getString(), rs.getInt(), rs.getLong()... ??

You can try the generic rs.getObject(columnName) method. Querying & mapping result set using Spring JdbcOperations might look like this:

List<Animal> animals = jdbcOperation.query("select * from animal", new RowMapper<Animal>() {
  @Overrides Animal mapRow(ResultSet rs, int rowNum) throws SQLException {
    Animal animal = new Animal();
    ResultSetMetaData meta = rs.getMetaData();
    for(int i = 1; i <= meta.getColumnCount(); i++) {
      String columnName = meta.getColumnName(i);
      animal.getProperties().put(columnName, rs.getObject(columnName));
    }
    return animal;
  }
});

Code above will not require re-compilation if you add/remove a column. You can also apply the same concept for updates / insertion. However retrieving the column value gets ugly. Since everything is stored as Object, you need to know what to cast it to.

// Getting this animal's name
String name = (String) animal.getProperties().get("name");

Other approach I can think of is to use NoSQL approach with which you don't even need to define a data schema.

like image 36
gerrytan Avatar answered Nov 15 '22 06:11

gerrytan