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]
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With