Association with columnPrefix giving SQLException invalid column name - java

I'm getting SQLException: invalid column name when trying to use an <association> tag with columnPrefix. e.g.
Example Result Maps:
<resultMap id="foo" type="Foo">
<result column="col1" property="col1"/>
</resultMap>
<resultMap ...>
<association property="myFoo"
notNullColumn="some_id"
resultMap="foo"
columnPrefix="BAR_"/>
</resultMap>
Example SQL:
select some_id, BAR_col1, ... from (someTables)
^^^^--prefixed columns...
If I take columnPrefix off and make a custom resultmap of foo that has bar_ embedded for each column, it works. What's going on here?

The problem was with the notNullColumn! It must follow two rules:
In the SQL statement, the column alias must start with the column prefix (so select BAR_some_id ...) even if it's not part of the associated result map (foo, in my case).
The notNullColumn declaration should not include the prefix. (so notNullColumn="BAR_some_id" is wrong. It has to be notNullColumn="some_id".)

Related

Is it possible to use Aggregate functions in MyBatis

I am a newbie to MyBatis. As per my knowledge every column is mapped to a property value while retrieving data from the database. So is it possible to use aggregate functions using MyBatis. To what property we will map the results? I have searched it everywhere But can't find any details about the usage of aggregate functions with MyBatis. If anyone can help, please.
Every column of the result set is mapped to a property. Therefore, it's just a matter of producing a SQL statement that does that.
For example:
<resultMap id="regionProfit" type="app.RegionProfitVO">
<result property="region" column="region" />
<result property="cnt" column="cnt" />
<result property="profit" column="profit" />
</resultMap>
<select id="getRegionProfit" resultMap="regionProfit">
select
region,
count(sales) as cnt,
sum(revenue) - sum(expenses) as profit
from sales
group by region
</select>
Now, in the Java code you could do something like:
List<RegionProfitVO> rp = sqlSession.selectList("getRegionProfit");
If you don't want to create another POJO, you can always retrieve the result as a list of java.util.Map<String, Object> but use resultType="hashmap" in mapper.xml file for that select query

How to map a DB column name that is a Java reserved word to a Java model object field using MyBatis?

So I have a table CoolTable with 2 columns: something and short.
And my model class named CoolClass reflects it with 2 fields: something and _short. short is a Java reserved keyword, so the field had to be prefixed with the underscore.
Now my mybatis mapper XML uses CoolClass like so:
<select id="getStuff" resultType="CoolClass">
SELECT * FROM CoolTable
</select>
<insert id = "insertStuff" parameterType = "CoolClass">
INSERT INTO CoolTable (something, short)
VALUES (#{something}, #{short})
</insert>
Now, when I getStuff and insertStuff only something column gets retrieved and inserted. short is always null.
I searched through all the MyBatis documentation but couldn't find anything relevant to help with this case.
How can I go about mapping short column to _short field of class?
Fixed it by using a ResultMap like so:
<resultMap id="coolResultMap" type="CoolClass">
<result property="_short" column="short"/>
</resultMap>
<select id="getStuff" resultMap="coolResultMap">
SELECT * FROM CoolTable
</select>

Mybatis2 to MyBatis3 Conversion - Results with Same Property

I am working on converting a large MyBatis2 map to a MyBatis3 map, and have run into a bit of an issue where I have a resultMap with multiple result elements using the same property attribute (and the class is generated from a WSDL outside of my control):
<resultMap id="blah" class="someWsdlGeneratedClass">
<result property="addressLine"
resultMap="addressLineOneListMap"
javaType="java.util.List" />
<result property="addressLine"
resultMap="addressLineTwoListMap"
javaType="java.util.List" />
</resultMap>
<resultMap id="addressLineXListMap" class="string">
<!-- Result maps are the same except for column -->
<result property="addressLine" column="COLUMN_X" />
</resultMap>
Notice that both properties are "addressLine".
This works fine for Mybatis2. However, if I try to use the same pattern for MyBatis3, I get an IllegalArgumentException: Result Maps collection already contains value for Mapper.mapper_resultMap[blah]_collection[addressLine]
<resultMap id="blah" type="someWsdlGeneratedClass">
<collection property="addressLine"
resultMap="addressLineOneListMap"
javaType="java.util.List" />
<collection property="addressLine"
resultMap="addressLineTwoListMap"
javaType="java.util.List" />
</resultMap>
I'd like to avoid writing a wrapper around the generated class in a Dto object, if possible, as that would result in a major refactoring effort in the project. Is there something I can add in the map itself?
You can add a 2nd setter (setAddressLine2) to your generated dto.
In which your code for it can just add to addressLine. ex:
void setAddressLine2(final List<Address> addressLine2) {
address.addAll(addressLine2);
}
If that's not possible you can try changing your query to return a union of the 2 columns
Without knowing your exact query it would look something like:
SELECT foo, addressLine1 as Address
FROM bar
UNION
SELECT foo, addressLine2 as Address
FROM bar
If that isn't possible then you need to create a test project and create an issue on https://github.com/mybatis/mybatis-3 and request a feature.
That option is probably best anyways as I'm not sure you are using it correctly. It seems that your 2nd example (using collection) is correct (conceptually at least. you still can't map to the same property using it) but the first won't behave as you explained it?

Is it possible to map POJO fields with names different from the table columns?

I am relatively new to ibatis. I know it is already upgraded to mybatis, but for some reason I have to use ibatis. My question is that "Is it possible to map POJO fields with names different from the table columns?"
I have a table, the mapping file, and the POJO class. I can successfully read data if my POJO class has names exactly the same as the columns in the table, but if I name the field something else, it does not work. I changed the corresponding getter and defined a resultMap in the mapping file like the following
<resultMap id="result" class="Subscriber">
<result column="AdvisorId" property="id" jdbcType="INTEGER"/>
<result column="FirstName" property="FirstName" jdbcType="VARCHAR"/>
<result column="LastName" property="LastName" jdbcType="VARCHAR"/>
<result column="EmailId" property="EmailId222" jdbcType="VARCHAR"/>
</resultMap>
<select id="getAll" resultMap="result">
SELECT AdvisorId,FirstName,LastName,EmailId FROM communication
</select>
Here as an example, I am trying to rename EmailId in my POJO field to EmailId222
In the Subscriber class you have to define getter and setter methods for the field. Be careful to use capital letters if necessary. Then you have to restart your application to make the changes work. In your case, you should implement:
getEmailId222(){...}
setEmailId222(String EmailId222){...}

Constructing resultMap to create different instances for the same collection

(Using MyBatis v3.0.4.)
I have a problem that I do not know how to solve. My object model is:
Location.java
public class Location {
// ... other content
private List addresses;
// ... other content
}
Address.java
public class Address {
public enum Type { POSTAL, POBOX, INPUT, CLEANSED }
private Type type;
private String line1;
// ... other content
}
My SQL is:
SELECT
// ... other content
postal_address_line_1,
postal_address_line_2,
postal_address_city,
cleansed_address_line_1,
cleansed_address_line_2,
cleansed_address_city,
// ... other content
How would I construct a resultMap that would plug the appropriate
columns into an address instance of the correct type and added to the
same list in Location.java? I would like to avoid having to add
another instance variable to Location.java just to hold a different
type of address.
Use a discriminator tag in your result map.
Look at the mybatis user guide. Search for "discriminator" you see more informations.
<resultMap id="vehicleResult" type="Vehicle">
<id property=”id” column="id" />
<result property="sharedPropA" column="shared_column"/>
<discriminator javaType="int" column="address_type">
<case value="1" resultMap="postalResultMap"/>
<case value="2" resultMap="inputResultMap"/>
<case value="3" resultMap="cleanResultMap"/>
<case value="4" resultMap="whatIsaCleansedAddressResultMap"/>
</discriminator>
</resultMap>
Addition 1:
You need to select the addresses as different rows.
i.e
select
postal_address_line_1 as line1,
postal_address_line_2 as line2,
postal_address_city as city,
type as 'POSTAL'
....
union
select
postal_address_line_1 as line1,
postal_address_line_2 as line2,
postal_address_city as city,
type as 'CLEANSED'
.....
then the built in enum type handler should set the type correctly.
Along the lines of Andy Pryor's suggestion, I was able to solve the problem by updating my SQL statement to something like the following:
SELECT
// ... other content
'POSTAL' as Postal_Address_Type,
postal_address_line_1,
postal_address_line_2,
postal_address_city,
'CLEANSED' as Cleansed_Address_Type,
cleansed_address_line_1,
cleansed_address_line_2,
cleansed_address_city,
// ... other content
Then update my resultMap to the following:
<resultMap ...>
//... other content
<association property="postalAddress" javaType="com.x.y.z.Address">
<result property="type" column="Postal_Address_Type"/>
<result property="line1" column="Address_Part_1_Name"/>
<result property="line2" column="Address_Part_2_Name"/>
//...other content
</association>
<association property="cleansedAddress" javaType="com.x.y.z.Address">
<result property="type" column="Cleansed_Address_Type"/>
<result property="line1" column="Address_Part_1_Name"/>
<result property="line2" column="Address_Part_2_Name"/>
//...other content
</association>
</resultMap>
Finally, within my Address class I am able to have setType(Type) and the inbuilt enumerated type handler does the magic. Within the Location class I can just have one list of instances of Address and the various setXXXAddress() methods can add to this list appropriately.
It is unfortunate that I cannot plug the columns into some sort of factory class but putting hard-coded types into the SQL statement isn't too dirty, in my opinion. The disadvantage is that I have introduced coupling between the domain model's Address.Type values and the SQL statement but this is kind of already there given that the resultMap SQL XML needs to hold the names of instance variables in the Address class anyway.

Categories