Skip to content
Advertisement

How to prevent unintended default values from MyBatis queries?

I have the following Java class:

// Java
class Example {
  boolean first;
  boolean second;
}

The class represents a database table called example. I can use the following SQL query to fetch Example instances from the table with MyBatis:

// SQL
select
  first as example_first,
  second as example_second
from example 

Let’s say that I will add a new field to the table and the class: boolean third. However, if I don’t remember to add the field to the select queries, it will have the default value of false. This could lead to unexpected results when checking the value. Same thing would happen also if a new query was created in the future but without all the fields.

One solution is to use Boolean instead of boolean. Then it would throw a NullPointerException if trying to evaluate third without the value present. Even this would still be prone to a developer mistake, i.e. a redundant null check. For example:

// Java
final Example e = database.fetchExampleWithoutThird(); // third field missing from query

if (e.third != null && e.third.booleanValue()) {
  // never executes
}

Is there some solution that would enforce fetching all property values?

Advertisement

Answer

You are hitting the limitation of Java here so the is no elegant solution to this without the help of a 3rd party library (which is still great and I highly recommend).

Use constructor injection and not property injection in result maps and also use lombok to automatically generate constructors for you (and/or lombok’s @NonNull checks if you use Boolean).

So if you have class:

@AllArgsConstructor  // this generates constructor with parameters for every field
class Example {
  boolean first;
  boolean second;
}

When you add a new field to it the constructor for it will be automatically changed to include that new field. Given that you use constructor injection that will force you to add new column to all queries using the class.

The mapping should be like this:

<resultMap id="exampleResultMap" type="Example">
  <constructor>
    <arg column="first" javaType="boolean"/>
    <arg column="second" javaType="boolean"/>
  </constructor>
</resultMap>

After the change the mybatis will complain during query execution that constructor with two parameters is not found or that fields are not present in the result and this will force you to change the query and/or mapping.

You can also use autoMapping=true in resultMap so that you don’t need to specify fields explicitly and you will need to update only the queries.

Advertisement