Java – how to use ibatis 2.3 4 effectively maps complex set attributes
I have a domain object that represents the 1: n relationship between database tables
public class ObservationWithData { private Observation observation; private Map<Integer,ElementValue> elementValues; // accessor methods ... }
Both observation and elementvalue contain
private int datetimeId; private int stationId; // accessor methods ...
I have a select query that connects two tables I want to use resultmap to group elementvalues with observations based on datetimeid and stationid, as shown in this (non working) example
<resultMap id="observationWithDataMap" class="ObservationWithData" groupBy="observation.datetimeId,observation.stationId"> <result property="observation" resultMap="Observationsql.observationMap" /> <result property="elementValues" resultMap="Elementsql.elementValueMap"/> </resultMap>
There are two problems First, the groupby tag does not allow nested syntax. The second ibatis requires that the grouping attribute be part of the Java collections API, and the map does not meet this standard
I can solve the first problem by adding datetimeid and stationid accessors. The second item can be solved by creating a set to write, and then add all items to the map after data extraction
<resultMap id="observationWithDataMap" class="ObservationWithData" groupBy="datetimeId,stationId"> <result property="stationId" column="station_id" /> <result property="datetimeId" column="datetime_id" /> <result property="observation" resultMap="Observationsql.observationMap" /> <result property="elementValueCollection" resultMap="Elementsql.elementValueMap"/> </resultMap> public class ObservationWithData { private Observation observation; private Map<Integer,ElementValue> elementValues; // this collection is used for database retrieval only; do not add to it private Collection<ElementValue> elementValueCollection; public void setStationId(int id) { } public int getStationId() { return observation==null?0:observation.getStationId(); } public void setDatetimeId(int id) { } public int getDatetimeId() { return observation==null?0:observation.getDatetimeId(); } public Map<Integer,ElementValue> getElementValues() { if (elementValues.size()==0 && elementValueCollection.size()>0) { for (ElementValue val : elementValueCollection) { elementValues.put(val.getElementId(),val); } elementValueCollection.clear(); } return elementValues; } public Collection<ElementValue> getElementValueCollection() { if (elementValueCollection.size()==0 && elementValues.size()>0) { elementValueCollection.addAll(elementValues.values()); elementValues.clear(); } return elementValueCollection; } ... }
However, I'm not very satisfied with this solution, because there are many public methods, and I don't want people to use them in their code ID setters are noops because these IDs are set during observation I can use observationwithdata to extend observation, but I design this class to facilitate the inheritance combination of each effective Java I can also synchronize maps and collections differently, but my idea is that I will let ibatis fill the collection, and then when I access the map, I will move all values into it and keep only one reference
Can anyone recommend a more elegant solution?
Edit:
I finally extracted a service layer to deal with this problem The service layer makes two database calls through Dao instead of one; The first retrieves all observations, and the second retrieves all elementvalues It constructs observationwithdata objects from these collections It also limits requests because this is a large data set
This is a bit clumsy and inefficient because I build objects "manually" However, since this is "hidden" in the service layer, I think it has less interference with API users, who can use neat domain objects
Solution
Did you try to write a Dao so that this is a two-phase process, first getting the observationwithdata objects and then populating them with elementvalues? It won't expand to a large number of lines, but depending on what you need to get it, it may make it cleaner on the outside
Another option is to get data from the database by mapping the results to package private objects with getters and setters, and then returning them to the caller as different classes without setters I'm not sure if this will help. I think it may even cause more trouble for the solution