How can I efficiently display complex collection properties in ibatis 2.3.4

I have a domain object that represents a 1: n ratio 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 joins two tables. I would like to use resultMap to group ElementValues ​​with observations based on datetimeId and stationId, as in this example (non-working).

 <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 with this. First, the groupBy tag does not allow nested syntax, and the second iBATIS requires that the grouped property be part of the Java collections API, and Map does not meet this criterion.

I can get around the first problem by adding datetimeId and accessId, and the second element can be solved by creating a collection for writing, and then add all the elements to the map after popping the data.

 <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; } ... } 

I am not very happy with this solution, because now there are many public methods that I do not want people to use their code. Identifiers are noops because these identifiers are set in the watch. I could have ObserveWithData extend Observation, but I designed this class to support composition over inheritance for efficient Java. I could also synchronize the map and the collection in different ways, but the idea is that I will let iBATIS fill the collection, and then when access is made to it, I will move all the values ​​to it, keeping only one link.

Can someone recommend a more elegant solution?

Edit:

As a result, I extracted the service level for this. The service level makes two database calls through the DAO instead of one; the first retrieves all Observations, and the second retrieves all ElementValues. It creates ObservationWithData objects from these collections. It also throttles the query as it is a large data set.

This is a bit awkward and inefficient because I manually create objects. However, since it is β€œhidden” at the service level, I feel that it is less intrusive for API users who get an object with an uncluttered domain to work with.

+4
source share
2 answers

Have you tried writing your DAO so these are two-step processes when you first retrieve ObservationWithData objects and then populate them with elementValues? It will not scale for a huge number of lines, but depending on what you need to extract, it may make it a little cleaner outside at the moment.

Another option would be to select data from the database by comparing the results for packing private objects using getters and setters, and then returning them to the caller as another class without setters. Not sure if this helps, I think it might even add more problems to the solution.

+1
source

We displayed the properties of the complex set using the groupBy attribute from ibatis, as used below -

 <resultMap id="actionResultsMap" class="Content" groupBy="objectID"> <result property="objectID" column="object_id"/> <result property="bvReferenceNo" column="reference_no"/> ....... <result property="countryId" resultMap="MyActions.countryMap"/> </resultMap> <resultMap id="countryMap" class="java.lang.Integer"> <result property="value" column="country"/> </resultMap> <select id="myActions" resultMap="actionResultsMap" parameterClass="java.util.HashMap"> SELECT object_id, reference_no, ........ country from sysObject so left outer join countryTable ct on ct.object_id = so.object_id </select> 

Here countryId is of type List<Integer> .

0
source

All Articles