ResultSet Mapper is a small lightweight library that allows you to map a ResultSet
object to a desired java object by passing in its type.
- Example usages
- Why use this library?
- Installation
- License
- Java version
Define a java object you want to map to. Use the @Column
annotation to override the FieldNamingStrategy
public class User {
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
private String email;
}
To map a ResultSet
you need an instance of ResultSetMapper
. The library provides a factory class ResultSetMapperFactory
that serves ResultSetMapper
with different FieldNamingStrategy
. The ResultSetMapper
defaults to the IdentityFieldNamingStrategy
.
// ResultSetMapper with IdentityFieldNamingStrategy
ResultSetMapper r = ResultSetMapperFactory.getResultSetMapperIdentity();
List<User> users = r.map(resultSet, User.class);
// more examples of out of the box factory calls for different field naming strategies
// ResultSetMapper with LowerCaseUnderscoreFieldNamingStrategy
ResultSetMapper r2 = ResultSetMapperFactory.getResultSetMapperLowerCaseUnderscore();
List<User> users = r2.map(resultSet, User.class);
// ResultSetMapper with LowerCaseDashesFieldNamingStrategy
ResultSetMapper r3 = ResultSetMapperFactory.getResultSetMapperLowerCaseDashes();
List<User> users = r3.map(resultSet, User.class);
The library provides out of the box a few field naming strategies.
Note: The provided strategies assumes you use camelCase
for your field names. It does not work if you use other naming styles. If this does not conform to your naming style, you have to implement your own field naming strategy.
This strategy leaves the field names unchanged.
This strategy maps the field names to lowercase with underscores. For instance firstName
would map to first_name
.
This strategy maps the field names to lowercase with dashes. For instance firstName
would map to first-name
.
It is possible to make your own field naming strategy. It is done by implementing the FieldNamingStrategy
interface. It has one function transform
which transforms the original field name. The concrete FieldNamingStrategy
implementation can then be injected through the constructor.
ResultSetMapper r = new ResultSetMapper(new CustomFieldNamingStrategy());
The library provides the ability to convert one type to another, allowing two incompatible types to work during mapping.
Using the @Convert
annotation you can define on class field level which attribute converter must be used.
Note: If there is no @Convert
annotation is present, the mapper will search for an appropriate attribute converter. If it has found one it will apply only if there is a @Converter
annotation applied on the attribute converter with autoApply
equaling true
.
public class User {
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
private String email;
@Convert(converter = TimestampToLocalDateTimeConverter.class)
private LocalDateTime creationDateTime;
}
Let's see how an attribute converter can be defined. As you can see below an attribute converter can be defined by implementing the AttributeConverter<S, T>
interface.
@Converter(autoApply = true)
public class TimestampToLocalDateTimeConverter implements AttributeConverter<Timestamp, LocalDateTime> {
@Override
public LocalDateTime convert(Timestamp value) {
return value.toLocalDateTime();
}
@Override
public Class<Timestamp> source() {
return Timestamp.class;
}
@Override
public Class<LocalDateTime> target() {
return LocalDateTime.class;
}
}
This interface has three methods.
convert
- the actual method that does the conversion of typeS
to typeT
.source
- source class, it should be of typeS
.target
- target class, it should be of typeT
.
The source
and target
is used internally to determine if the attribute converter is appropriate to be used for a field.
An attribute converter must always be annotated with the @Converter
annotation to let the mapper know it is an attribute converter.
The annotation can accept one value which is autoApply
. autoApply
determines whether to automatically apply the converter when possible. The default value is false
The library currently provides a few out of the box attribute converters.
TimestampToLocalDateTimeConverter
- converts a value of typejava.sql.Timestamp
tojava.time.LocalDateTime
DateToLocalDateConverter
- converts a value of typejava.sql.Date
tojava.time.LocalDate
TimeToLocalTimeConverter
- converts a value of typejava.sql.Time
tojava.time.LocalTime
You can also define your own attribute converters by implementing the AttributeConverter<S, T>
interface.
After having defined it you must register this attribute converter. You can register it by calling the registerAttributeConverter
on the ResultSetMapper
.
The library allows you to annotate class fields with @Ignore
. This annotation can be used on a field to let the ResultSetMapper
know that it can skip this field when mapping. This means that the ResultSetMapper
will not try to retrieve the value from the ResultSet
for the annotated field.
Let me show you an example.
public class User {
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
private String email;
@Ignore
private List<UserPermission> userPermissions;
}
As you can see above the User
class has an additional List<UserPermission>
object. To retrieve that it requires an additional query and can not be mapped when mapping the initial ResultSet
. By using the @Ignore
annotation it can let the mapper know that it does not have to be mapped when trying to map the User
class.
The library has very extensive logging at every logging level. From TRACE
to ERROR
, the logging level is default set on INFO
.
This means only logging messages with log level of INFO
and above will be logged.
It is possible to suppress warnings for particular classes.
For instance the mapper will throw warning messages when a field can not be found in the ResultSet
.
It would be annoying to be spammed with warning message for every object that is to be mapped.
The library provides the @SuppressWarnings
annotations to "suppress" these warning messages.
@SuppressWarnings
public class User {
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
private String email;
}
This will suppress all warnings for every field in the annotated class.
public class User {
private int id;
@SuppressWarnings
@Column(name = "first_name")
private String firstName;
@SuppressWarnings
@Column(name = "last_name")
private String lastName;
private String email;
}
It is also possible to suppress warnings at field level. The warnings will be suppressed for that particular annotated field.
This library makes it easy to map to a ResultSet
to your desired java model object. It can be done with only 1 line of code! It saves you a lot of duplicate code when mapping every query.
List<User> users = new ArrayList();
while (resultSet.next()){
User user = new User();
user.setId(resultSet.getInt("id");
user.setFirstName(resultSet.getString("first_name"));
user.setLastName(resultSet.getString("last_name"));
user.setEmail(resultSet.getString("email"));
users.add(user);
}
vs
List<User> users = r.map(resultSet, User.class);
Way more cleaner, right? Imagine doing the first example for every different query, that would be a lot of code..
The library also takes care of all the exception handling and provides very extensive logging, making it very easy to spot errors when it occurs.
<dependency>
<groupId>nl.jiankai</groupId>
<artifactId>resultset-mapper</artifactId>
<version>1.6.2</version>
</dependency>
implementation 'nl.jiankai:resultset-mapper:1.6.2'
See the LICENSE file for the license rights and limitations (MIT).
The library uses Java 11.