- 1. What is Spring-JDBC-ROMA?
- 2. Features
- 3. Installation
- 4. Usage
- 4.1. Default configurations
- 4.2. Primitive Typed Field Features
- 4.3. Date/Timestamp Typed Field Features
- 4.4. Clob Typed Column Features
- 4.5. Blob Typed Column Features
- 4.6. Enum Typed Field Features
- 4.7. Field Based Features Configuration Features
- 4.8. Class (or Type) Based Configuration Features
- 4.9. RXEL (ROMA Expression Language)
- 4.10. Complex Typed Field Features
- 4.11. Conditional Lazy Feature
- 4.12. Conditional Lazy-Load Feature
- 4.13. Conditional Ignore Feature
- 4.14. Other Features
- 5. A Simple Example
- 6. Roadmap
- What is Spring-JDBC-ROMA? =======
Spring-JDBC-ROMA is a rowmapper extension for Spring-JDBC module.
There is already a rowmapper named org.springframework.jdbc.core.BeanPropertyRowMapper
for binding
resultset attributes to object. But it is reflection based and can cause performance problems as Spring developers said.
However Spring-JDBC-ROMA is not reflection based and it is byte code generation (with CGLib and Javassist)
based rowmapper. It generates rowmapper on the fly like implementing as manual so it has no performance overhead.
It also supports object relations as lazy and eager. There are other lots of interesting features and
these features can be customized with developer's extended classes. This is the web site URL https://github.com/serkan-ozal/spring-jdbc-roma-impl.
-
All primitive types, string, enum, date/timestamp, clob, blob, collections and complex objects are supported.
-
Writing your custom class (or type) based field generator factory, object creater, object processor, table name resolver, column name resolver implementations and customizable data source, schema, table names are supported.
-
Writing your custom field based mapper, SQL based binder, expression language based binder and custom binder implementations are supported.
-
Lazy or eager field accessing is supported. Lazy support can be configured as conditional and conditions can be provided as expression language or as custom lazy condition provider implementations. If lazy condition is not enable, specified field is not handled as lazy and set as eager while creating root entity.
-
Loading lazy fields can be enable or disable at runtime dynamically by using key based, expression based and custom implementation based approaches. If lazy-load condition is not enable, specified lazy field is not loaded no matter field is configured as lazy.
-
Ignoring fields can be enable or disable at runtime dynamically by using key based, expression based and custom implementation based approaches. This feature is very useful in some use-cases. For example, this feature can be used to ignore some fields while serializing to JSON at your specified controller and this behaviour doesn't effect other controllers.
-
Writing field access definitions as REXL (ROMA Expression Language) or as compilable Java code in annotation. XML and properties file configuration support will be added soon.
In your pom.xml, you must add repository and dependency for Spring-JDBC-ROMA. You can change spring.jdbc.roma.version to any existing spring-jdbc-roma library version.
...
<properties>
...
<spring.jdbc.roma.version>2.0.0.RELEASE</spring.jdbc.roma.version>
...
</properties>
...
<dependencies>
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc-roma</artifactId>
<version>${spring.jdbc.roma.version}</version>
</dependency>
...
</dependencies>
...
<repositories>
...
<repository>
<id>serkanozal-maven-repository</id>
<url>https://github.com/serkan-ozal/maven-repository/raw/master/</url>
</repository>
...
</repositories>
...
And finally, in your Spring context xml file add following configuration to make your Spring context automatically aware of Spring-JDBC-ROMA.
...
<import resource="classpath*:roma-context.xml"/>
...
You must get your row mapper via service class (org.springframework.jdbc.roma.impl.service.RowMapperService
) of ROMA.
@Autowired
RowMapperService rowMapperService;
...
RowMapper<User> userRowMapper = rowMapperService.getRowMapper(User.class);
Default configurations are stored and accessed by org.springframework.jdbc.roma.api.config.DefaultConfigs
class.
Configurable values as default are:
-
Datasource name: Name of the datasource to connect. Initial value of default datasource name is
dataSource
. So if you have defined your datasource named asdataSource
, you don't need to configure this value. -
Schema name: Name of the schema name in DB to connect. Initial value of default datasource name is
null
. So default schema of DB is used. If default schema of your connected DB is used, you don't need to configure this value.
Using default values are helpful if there are so many entities using same datasource and schema name configurations. In addition to default values, you must configure these values based on entity and this will be mentioned in sections below. Note that initial default
Default configurations can be configured programatically or in context xml of Spring like:
...
<bean id="defaultConfigs" class="org.springframework.jdbc.roma.api.config.DefaultConfigs">
<property name="defaultDataSourceName" value="myDataSource"/>
<property name="defaultSchemaName" value="myDBSchema"/>
</bean>
...
boolean
byte
char
short
int
float
long
double
java.lang.Boolean
java.lang.Byte
java.lang.Character
java.lang.Short
java.lang.Integer
java.lang.Float
java.lang.Long
java.lang.Double
java.lang.String
typed fields are automatically mapped to result set in row mapper.
java.util.Date
typed fields are automatically mapped to result set as Date in row mapper. But if mapped column in database is defined as Timestamp typed, you can configure this field by using @RowMapperTimeField
annotation as:
@RowMapperTimeField(asTimestamp = true)
private Date date;
In addition, java.sql.Timestamp
typed fields are automatically mapped to result set as Timestamp in row mapper with no additional configuration.
CLOB
typed column can be mapped to java.lang.String
typed field by using @RowMapperClobField
annotation like this:
@RowMapperClobField
private String address;
BLOB
typed column can be mapped to byte[]
typed field by using @RowMapperBlobField
annotation like this:
@RowMapperBlobField
private byte[] image;
By default, enums are mapped by using numeric column value as ordinal of enum. For example, you have an enum named UserType
like this:
public enum UserType {
ADMIN,
MEMBER,
GUEST
}
If numeric value of column is 1
, value is mapped to enum field as MEMBER
.
Enum mappings can be configured by using @RowMapperEnumField
annotation.
- constantsAndMaps: This feature is now depracated from version 2.0. With this configuration feature, you can map numeric value of column to String value of enum.
For example:
@RowMapperEnumField(constantsAndMaps = {"1:ADMIN", "2:MEMBER", "3:GUEST"})
private UserType userType;
- enumStartValue: With this configuration feature, numeric column value is mapped to enum ordinal by using start value as first element of enum. For example, you have an enum named
Language
like this:
public enum Language {
TURKISH,
ENGLISH,
GERMAN,
...
}
and enum field is declared as like:
@RowMapperEnumField(enumStartValue = 1)
private Language language;
If numeric value of column is 1
, value is mapped to enum field as TURKISH
. Because enumStartValue
is 1
and actual ordinal will be 1 - 1 = 0
. So mapped value will be TURKISH
.
- useStringValue: By default, column value is mapped to enum value with its numeric value. With this configuration feature, column value is mapped to enum value with its string value by using name of enum value. For example, you have an enum named
Religion
like this:
public enum Religion {
MUSLIM,
JEWISH,
CHRISTIAN_CAHTOLICS,
CHRISTIAN_ORTHODOX,
CHRISTIAN_PROTESTANT,
ATHEIST,
OTHER;
}
and enum field is declared as like:
@RowMapperEnumField(useStringValue = true)
private Religion religion;
If string value of column is "MUSLIM"
, value is mapped to enum field as MUSLIM
.
- defaultIndex: With this configuration feature, if column value is empty (null), this index value will be used as ordinal of enum value for default value of field. For example, you have an enum named
UserType
like this:
public enum UserType {
ADMIN,
MEMBER,
GUEST
}
and enum field is declared as like:
@RowMapperEnumField(defaultIndex = 2)
private UserType userType;
If column value is empty (null), value is mapped to enum field as GUEST
. Because defaultIndex
is 2
and ordinal will used as 2
. So mapped value will be GUEST
.
- defaultValue: With this configuration feature, if column value is empty (null), this index value will be used as name of enum value for default value of field. For example, you have an enum named
UserType
like this:
public enum UserType {
ADMIN,
MEMBER,
GUEST
}
and enum field is declared as like:
@RowMapperEnumField(defaultValue = "GUEST")
private UserType userType;
If column value is empty (null), value is mapped to enum field as GUEST
. Since defaultValue
is "GUEST"
, mapped value will be GUEST
.
- mapViaNumericMapper: With this configuration feature by using
@RowMapperEnumNumericMapper
annotation in@RowMapperEnumField
annotation, you can use your custom mapper implementations by implementingorg.springframework.jdbc.roma.api.config.provider.annotation.RowMapperEnumField.NumericEnumMapper
interface for numeric column values. Instance of your implementation is created once and used as singleton. For example, you have an enum namedBloodType
like this:
public enum BloodType {
TYPE_A_RH_POSITIVE(1),
TYPE_A_RH_NEGATIVE(2),
TYPE_B_RH_POSITIVE(3),
TYPE_B_RH_NEGATIVE(4),
TYPE_AB_RH_POSITIVE(5),
TYPE_AB_RH_NEGATIVE(6),
TYPE_0_RH_POSITIVE(7),
TYPE_0_RH_NEGATIVE(8);
int code;
BloodType(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
your custom numeric mapper implementation is declared as like:
public class BloodTypeEnumMapper implements RowMapperEnumField.NumericEnumMapper<BloodType> {
@Override
public BloodType map(Integer value) {
for (BloodType bt : BloodType.values()) {
if (bt.getCode() == value) {
return bt;
}
}
return null;
}
}
and enum field is declared as like:
@RowMapperEnumField(
mapViaNumericMapper =
@RowMapperEnumNumericMapper(
mapper = BloodTypeEnumMapper.class))
private BloodType bloodType;
- mapViaStringMapper: With this configuration feature by using
@RowMapperEnumStringMapper
annotation in@RowMapperEnumField
annotation, you can use your custom mapper implementations by implementingorg.springframework.jdbc.roma.api.config.provider.annotation.RowMapperEnumField.StringEnumMapper
interface for numeric column values. Instance of your implementation is created once and used as singleton. For example, you have an enum namedMaritalStatus
like this:
public enum MaritalStatus {
SINGLE,
ENGAGED,
MARRIED,
DIVORCED;
}
your custom numeric mapper implementation is declared as like:
public class MaritalStatusEnumMapper implements RowMapperEnumField.StringEnumMapper<MaritalStatus> {
@Override
public MaritalStatus map(String value) {
for (MaritalStatus ms : MaritalStatus.values()) {
if (ms.name().equalsIgnoreCase(value)) {
return ms;
}
}
return null;
}
}
and enum field is declared as like:
@RowMapperEnumField(
mapViaStringMapper =
@RowMapperEnumStringMapper(
mapper = MaritalStatusEnumMapper.class))
private MaritalStatus maritalStatus;
- mapViaNumericValueNumericMappings: With this configuration feature by using
@RowMapperEnumAutoMapper
annotation in@RowMapperEnumField
annotation, you can map numeric column values with ordinals of enum. For example, you have an enum namedOccupation
like this:
public enum Occupation {
OTHER(0),
ARCHITECT(100),
DOCTOR(200),
ENGINEER(300),
LAWYER(400),
MUSICIAN(500),
STUDENT(600),
TEACHER(700);
int code;
Occupation(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
and enum field is declared as like:
@RowMapperEnumField(
mapViaAutoMapper =
@RowMapperEnumAutoMapper(
mapViaNumericValueNumericMappings = {
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 0, value = 0),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 1, value = 100),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 2, value = 200),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 3, value = 300),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 4, value = 400),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 5, value = 500),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 6, value = 600),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 7, value = 700)}))
private Occupation occupation;
- mapViaNumericValueStringMappings: With this configuration feature by using
@RowMapperEnumAutoMapper
annotation in@RowMapperEnumField
annotation, you can map numeric column values with names of enum. For example, you have an enum namedOccupation
like this:
public enum Occupation {
OTHER(0),
ARCHITECT(100),
DOCTOR(200),
ENGINEER(300),
LAWYER(400),
MUSICIAN(500),
STUDENT(600),
TEACHER(700);
int code;
Occupation(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
and enum field is declared as like:
@RowMapperEnumField(
mapViaAutoMapper =
@RowMapperEnumAutoMapper(
mapViaNumericValueStringMappings = {
@RowMapperEnumNumericValueStringMapping(mappingValue = "OTHER", value = 0),
@RowMapperEnumNumericValueStringMapping(mappingValue = "ARCHITECT", value = 100),
@RowMapperEnumNumericValueStringMapping(mappingValue = "DOCTOR", value = 200),
@RowMapperEnumNumericValueStringMapping(mappingValue = "ENGINEER", value = 300),
@RowMapperEnumNumericValueStringMapping(mappingValue = "LAWYER", value = 400),
@RowMapperEnumNumericValueStringMapping(mappingValue = "MUSICIAN", value = 500),
@RowMapperEnumNumericValueStringMapping(mappingValue = "STUDENT", value = 600),
@RowMapperEnumNumericValueStringMapping(mappingValue = "TEACHER", value = 700)}))
private Occupation occupation;
- mapViaStringValueNumericMappings: With this configuration feature by using
@RowMapperEnumAutoMapper
annotation in@RowMapperEnumField
annotation, you can map string column values with ordinals of enum. For example, you have an enum namedEducation
like this:
public enum Education {
PRIMARY_SCHOOL,
SECONDARY_SCHOOL,
HIGH_SCHOOL,
BACHELOR,
MASTER,
PHD,
ASSOCIATE_PROFESSOR,
PROFESSOR,
OTHER;
}
and enum field is declared as like:
@RowMapperEnumField(
mapViaAutoMapper =
@RowMapperEnumAutoMapper(
mapViaStringValueNumericMappings = {
@RowMapperEnumStringValueNumericMapping(mappingIndex = 0, value = "PRIMARY_SCHOOL"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 1, value = "SECONDARY_SCHOOL"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 2, value = "HIGH_SCHOOL"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 3, value = "BACHELOR"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 4, value = "MASTER"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 5, value = "PHD" ),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 6, value = "ASSOCIATE_PROFESSOR"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 7, value = "PROFESSOR"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 8, value = "OTHER")}))
private Education education;
- mapViaStringValueStringMappings: With this configuration feature by using
@RowMapperEnumAutoMapper
annotation in@RowMapperEnumField
annotation, you can map string column values with names of enum. For example, you have an enum namedEducation
like this:
public enum Education {
PRIMARY_SCHOOL,
SECONDARY_SCHOOL,
HIGH_SCHOOL,
BACHELOR,
MASTER,
PHD,
ASSOCIATE_PROFESSOR,
PROFESSOR,
OTHER;
}
and enum field is declared as like:
@RowMapperEnumField(
mapViaAutoMapper =
@RowMapperEnumAutoMapper(
mapViaStringValueStringMappings = {
@RowMapperEnumStringValueStringMapping(mappingValue = "PRIMARY_SCHOOL", value = "PRIMARY_SCHOOL"),
@RowMapperEnumStringValueStringMapping(mappingValue = "SECONDARY_SCHOOL", value = "SECONDARY_SCHOOL"),
@RowMapperEnumStringValueStringMapping(mappingValue = "HIGH_SCHOOL", value = "HIGH_SCHOOL"),
@RowMapperEnumStringValueStringMapping(mappingValue = "BACHELOR", value = "BACHELOR"),
@RowMapperEnumStringValueStringMapping(mappingValue = "MASTER", value = "MASTER"),
@RowMapperEnumStringValueStringMapping(mappingValue = "PHD", value = "PHD" ),
@RowMapperEnumStringValueStringMapping(mappingValue = "ASSOCIATE_PROFESSOR", value = "ASSOCIATE_PROFESSOR"),
@RowMapperEnumStringValueStringMapping(mappingValue = "PROFESSOR", value = "PROFESSOR"),
@RowMapperEnumStringValueStringMapping(mappingValue = "OTHER", value = "OTHER")}))
private Education education;
General field configurations can be done by using @RowMappeField
annotation.
By default, you don't need to specify column name for field if you use similar names for field and column in database. For similarity between field name and column name, default column name resolver generates some possible column names by using field name and check them by connecting to related table at database. Generated possible column names are in
- columnName
- columname
- COLUMNNAME
- column_Name
- colum_name
- COLUMN_NAME
formats.
For example, if you have a field named birthDate
, generated possible column names will be
- birthDate
- birthdate
- BIRTHDATE
- birth_Date
- birth_date
- BIRTH_DATE
- columnName: With this configuration feature, you can specify column name of field to be mapped.
For example:
@RowMappeField(columnName = "username")
private String username;
- fieldGenerator: With this configuration feature, you can use your custom field generator implementations by implementing
org.springframework.jdbc.roma.api.generator.RowMapperFieldGenerator
interface for generating mapping statement. Instance of your implementation is created once and used as singleton. In you implementation, you can
- refer to created entity by using
RowMapperFieldGenerator.GENERATED_OBJECT_NAME
property, - refer to resultset by using
RowMapperFieldGenerator.RESULT_SET_ARGUMENT
property, - refer to row number by using
RowMapperFieldGenerator.ROW_NUM_ARGUMENT
property.
For example:
@RowMappeField(fieldGenerator = UsernameFieldGenerator.class)
private String username;
and your custom field generator implementation is declared as like:
public class UsernameFieldGenerator implements RowMapperFieldGenerator {
@Override
public String generateFieldMapping(Field f) {
return
RowMapperFieldGenerator.GENERATED_OBJECT_NAME +
".setUsername" +
"(" +
RowMapperFieldGenerator.RESULT_SET_ARGUMENT + ".getString(\"username\")" +
");";
}
}
- fieldMapper: With this configuration feature, you can use your custom field mapper implementations by implementing
org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperField.RowMapperFieldMapper
interface for mapping specified field from resultset. Instance of your implementation is created once and used as singleton.
For example:
@RowMapperField(fieldMapper = RoleNameFieldMapper.class)
private String name;
and your custom field mapper implementation is declared as like:
public class RoleNameFieldMapper implements RowMapperFieldMapper<Role> {
private static final Logger logger = Logger.getLogger(RoleNameFieldMapper.class);
@Override
public void mapField(Role role, String fieldName, ResultSet rs, int rowNum) {
try {
role.setName(rs.getString("name"));
}
catch (SQLException e) {
logger.error("Error occured while mapping field " + fieldName +
" in Role object from resultset", e);
}
}
}
General class (or type) configurations can be done by using @RowMappeClass
annotation.
- dataSourceName: With this configuration feature, you can specify name of the datasource to connect for this class (or type). Initial value of default datasource name is
dataSource
. So if you have defined your datasource named asdataSource
, you don't need to configure this value.
For example:
@RowMapperClass(dataSourceName = "myDataSource")
public class User {
...
}
- schemaName: With this configuration feature, you can specify name of the schema in database to connect for this class (or type). Initial value of default datasource name is
null
. So default schema of DB is used. If default schema of your connected DB is used, you don't need to configure this value.
For example:
@RowMapperClass(schemaName = "Production")
public class User {
...
}
- tableName: With this configuration feature, you can specify name of the table to map this class (or type). By default, you don't need to specify table name for class if you use similar names for class and table in database. For similarity between class name and table name, default table name resolver generates some possible table names by using class name and check them by connecting to related table at database. Generated possible table names are in
- TableName
- tablename
- TABLENAME
- Table_Name
- table_name
- TABLE_NAME
formats.
For example, if you have a class named SystemLog
, generated possible table names will be
- SystemLog
- systemlog
- SYSTEMLOG
- System_Log
- system_log
- SYSTEM_LOG
For example:
@RowMapperClass(tableName = "USERS")
public class User {
...
}
- fieldGeneratorFactory: With this configuration feature, you can use your custom field generator factory implementations by implementing
org.springframework.jdbc.roma.api.factory.RowMapperGeneratorFactory
interface for creating field generators. Instance of your implementation is created once and used as singleton. For example you have a class namedUser
and you will use your custom field generator factory class.
For example:
@RowMapperClass(fieldGeneratorFactory = UserFieldGeneratorFactory.class)
public class User {
...
}
and your custom field generator factory implementation is declared as like:
public class UserFieldGeneratorFactory implements RowMapperFieldGeneratorFactory<User> {
@Override
public RowMapperFieldGenerator<User> createRowMapperFieldGenerator(Field f) {
if (f.getType().equals(int.class) || f.getType().equals(Integer.class)) {
return new MyIntegerFieldGenerator(f);
}
...
}
}
- objectCreater: With this configuration feature, you can use your custom object creater implementations by implementing
org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperClass.RowMapperObjectCreater
interface for creating entities. Instance of your implementation is created once and used as singleton. For example you have a class namedRole
and you will use your custom object creater class.
For example:
@RowMapperClass(objectCreater = RoleObjectCreater.class)
public class Role {
...
}
and your custom object creater implementation is declared as like:
public class RoleObjectCreater implements RowMapperObjectCreater<Role> {
@Override
public Role createObject(Class<Role> clazz) {
return new Role();
}
}
- objectProcessor: With this configuration feature, you can use your custom object processor implementations by implementing
org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperClass.RowMapperObjectProcessor
interface for creating entities. Instance of your implementation is created once and used as singleton. Object processor class is called after all fields have been mapped from result set before object is returned from rowmapper. For example you have a class namedUser
and you will use your custom object processor class. By your custom object processor class, you will setage
field that is not present in database table ofUser
object since it can be calculated by usingbirthDate
field.
For example:
@RowMapperClass(objectProcessor = UserObjectProcessor.class)
public class User {
...
}
and your custom object processor implementation is declared as like:
public class UserObjectProcessor implements RowMapperObjectProcessor<User> {
@SuppressWarnings("deprecation")
@Override
public void processObject(User user, ResultSet rs, int rowNum) {
if (user.getBirthDate() != null) {
user.setAge((byte)(Calendar.getInstance().getTime().getYear() - user.getBirthDate().getYear()));
}
}
}
- columnNameResolver: With this configuration feature, you can use your custom column name resolver implementations by implementing
org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperClass.RowMapperColumnNameResolver
interface for resolving column name of fields. Instance of your implementation is created once and used as singleton. For example you have a class namedUser
and you will use your custom field generator factory class.
For example:
@RowMapperClass(columnNameResolver = UserColumnNameResolver.class)
public class User {
...
}
and your custom column name resolver implementation is declared as like:
public class UserColumnNameResolver implements RowMapperColumnNameResolver {
@Override
public String resolveColumnName(Field f) {
return f.getName().toLowerCase();
}
}
- tableNameResolver: With this configuration feature, you can use your custom table name resolver implementations by implementing
org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperClass.RowMapperTableNameResolver
interface for resolving table name of classes. Instance of your implementation is created once and used as singleton. For example you have a class namedUser
and you will use your custom field generator factory class.
For example:
@RowMapperClass(tableNameResolver = UserTableNameResolver.class)
public class User {
...
}
and your custom table name resolver implementation is declared as like:
public class UserTableNameResolver implements RowMapperTableNameResolver {
@Override
public String resolveTableName(Class<?> clazz) {
return clazz.getSimpleName().toUpperCase();
}
}
ROMA has a specific expression language named ROMA Expression Language (REXL) for customizing some implementations in short like any other expression languages such as Spring expression language.
With @
sign you can use Spring beans in your expression language.
For example:
@{userDao}.list()
expression is executed as calling list
method in Spring bean with id userDao
.
With $
sign you can use properties of created entity in your expression language.
For example:
@{roleDao}.list(${id})
expression is executed as calling list
method in Spring bean with id roleDao
by passing id
property of created entity as argument.
With #
sign you can get any attribute of any object in your expression language.
For example:
@{userDao}.getAdmin().#{name}
expression is executed as calling getAdmin
method in Spring bean with id userDao
and return name
attribute of returning object (possibly User entity) from getAdmin
call.
With &
sign you can use result set attributes in your expression language. But in your expression, you must indicate type of the requested attribute between [
and ]
signs.
For example:
&{[int]enable} == 0 ? "false" : "true"
expression is executed as calling getInt("enable")
method in result set and checks its value to return true
or false
.
- provideViaExpressionProvider: With this configuration feature, you can use RXEL (ROMA Expression Language) expression language for providing field value.
For example:
@RowMapperObjectField(
provideViaExpressionProvider =
@RowMapperExpressionProvider(expression = "@{roleDAO}.getUserRoleList(${id})"))
private List<Role> roles;
- provideViaSqlProvider: With this configuration feature, you can specify SQL query will be executed to provide value of field by using limited supported version of RXEL. Only property (
${...}
) and resultset (&{[...]...}
) based expressions are supported in specified SQL query. If your field is collection typed (Only List type is supported for SQL provide feature), you must specify element type by usingentityType
property. In addition, you can specify name of datasource where SQL query will be executed by usingdataSourceName
property.
For example:
RowMapperObjectField(
provideViaSqlProvider =
@RowMapperSqlProvider(
provideSql =
"SELECT p.* FROM PERMISSION p WHERE p.ID IN " +
"(" +
"SELECT rp.PERMISSION_ID FROM role_permission rp WHERE rp.ROLE_ID = ${id}" +
") ORDER BY p.name",
entityType = Permission.class),
...)
private List<Permission> permissions;
Also, there are a way to customizing SQL query with its parameters by using your custom SQL query info provider implementations by implementing org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperSqlProvider.RowMapperSqlQueryInfoProvider
interface for providing SQL query info. Instance of your implementation is created once and used as singleton. Your custom SQL query info provider class returns a org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperSqlProvider.SqlQueryInfo
typed object and this objects contains SQL query to execute and its parameters as Object[]
array. Note that RXEL is not supported in SQL query for this feature and you must use ?
as placeholder for parameter in query.
For example:
@RowMapperObjectField(
provideViaSqlProvider =
@RowMapperSqlProvider(
sqlQueryInfoProvider = UserAccountInfoSqlQueryInfoProvider.class))
private AccountInfo accountInfo;
and your custom SQL query info provider implementation is declared as like:
public class UserAccountInfoSqlQueryInfoProvider implements RowMapperSqlQueryInfoProvider<User> {
@Override
public SqlQueryInfo provideSqlQueryInfo(User user, String fieldName) {
return
new SqlQueryInfo(
"SELECT a.* FROM ACCOUNT_INFO a WHERE a.ID IN " +
"(" +
"SELECT ua.account_info_id FROM USER_ACCOUNT_INFO ua WHERE ua.user_id = ?" +
")",
new Object[] { user.getId() });
}
}
- provideViaCustomProvider: With this configuration feature, you can use your custom field value provider implementations by implementing
org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperCustomProvider.RowMapperFieldProvider
interface for providing value of field. Instance of your implementation is created once and used as singleton.
For example:
@RowMapperObjectField(
provideViaCustomProvider =
@RowMapperCustomProvider(
fieldProvider = UserPhoneNumberFieldProvider.class))
private String phoneNumber;
and your custom field provider implementation is declared as like:
public class UserPhoneNumberFieldProvider implements RowMapperFieldProvider<User, String> {
private final static Logger logger = Logger.getLogger(UserPhoneNumberFieldProvider.class);
@Override
public String provideField(User user, String fieldName, ResultSet rs, int rowNum) {
try {
return rs.getString("phone_number");
}
catch (SQLException e) {
logger.error("Error occured while mapping field " + fieldName + " in User object from resultset", e);
return null;
}
}
}
-
lazy: With this configuration feature, you can configure this field as lazy permanently if
lazyCondition
is not specified. Note that specified field type must notfinal
class. -
lazyCondition: With this configuration feature, you can configure conditional lazy feature mentioned at section
4.11. Conditional Lazy Feature
. -
lazyLoadCondition: With this configuration feature, you can configure conditional lazy-load feature mentioned at section
4.12. Conditional Lazy-Load Feature
. -
ignoreCondition: With this configuration feature, you can configure conditional ignore feature mentioned at section
4.13. Conditional Ignore Feature
.
Conditional lazy feature can be configured by using @RowMapperLazyCondition
annotation in @RowMapperObjectField
annotation with lazyCondition
attribute. With conditional lazy feature, you can control lazy feature of any field as dynamic at runtime.
- provideViaPropertyBasedProvider: With this configuration feature, you can attach a specific property to a lazy condition and enable/disable condition of this property is used to evaluate condition.
For example:
@RowMapperObjectField(
...
lazyCondition =
@RowMapperLazyCondition(
provideViaPropertyBasedProvider =
@RowMapperPropertyBasedLazyConditionProvider(
propertyName = "creditCardInfoLazyCondition")))
private CreditCardInfo creditCardInfo;
To enable/disable lazy condition property, there are two ways:
- You can enable/disable lazy condition property by using
enableLazyConditionProperty
anddisableLazyConditionProperty
methods oforg.springframework.jdbc.roma.impl.service.RowMapperService
service class.
For example you can get row mapper service class as:
@Autowired
private RowMapperService;
and you can enable lazy condition property as:
rowMapperService.enableLazyConditionProperty("creditCardInfoLazyCondition");
or you can disable lazy condition property as:
rowMapperService.disableLazyConditionProperty("creditCardInfoLazyCondition");
- You can enable/disable lazy condition property automatically by using
@RowMapperPropertyBasedLazyConditionAware
annotation on any method of any class in Spring context before database access code is executed. To enable/disable this property on start of this method and on end of this method can be configured byoptions
property.
For example you can setup lazy condition configuration automatically by annotation like:
@Override
@RowMapperPropertyBasedLazyConditionAware(
propertyName = "creditCardInfoLazyCondition",
options = RowMapperPropertyBasedLazyConditionAware.ENABLE_ON_START |
RowMapperPropertyBasedLazyConditionAware.DISABLE_ON_FINISH)
public CreditCardInfo getUserCreditCardInfo(Long userId) {
...
}
- provideViaExpressionBasedProvider: With this configuration feature, you can use RXEL (ROMA Expression Language) expression language to evaluate lazy condition.
For example:
@RowMapperObjectField(
...
lazyCondition =
@RowMapperLazyCondition(
provideViaExpressionBasedProvider =
@RowMapperExpressionBasedLazyConditionProvider(
expression = "${name}.equals(\"Member\")")))
private List<Permission> permissions;
- provideViaCustomProvider: With this configuration feature, you can use your custom lazy condition evaluater implementations by implementing
org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperLazyCondition.RowMapperLazyConditionProvider
interface for evaluating lazy condition as custom. Instance of your implementation is created once and used as singleton.
For example:
@RowMapperObjectField(
...
lazyCondition =
@RowMapperLazyCondition(
provideViaCustomProvider =
@RowMapperCustomLazyConditionProvider(
lazyConditionProvider = UserRolesLazyConditionProvider.class)))
private List<Role> roles;
and your custom lazy condition checker implementation is declared as like:
public class UserRolesLazyConditionProvider implements RowMapperLazyConditionProvider<User> {
private final static Logger logger = Logger.getLogger(UserRolesLazyConditionProvider.class);
@Override
public boolean evaluateCondition(User user, String fieldName, ResultSet rs, int rowNum) {
boolean conditionResult = user.getId() % 2 == 0;
logger.debug("Evaluated lazy condition of field " + "\"" + fieldName + "\"" +
" for user with id " + "\"" + user.getId() + "\"" + " as " + "\"" + conditionResult + "\"");
return conditionResult;
}
}
Conditional lazy-load feature can be configured by using @RowMapperLazyLoadCondition
annotation in @RowMapperObjectField
annotation with lazyLoadCondition
attribute. With conditional lazy-load feature, you can control real object loading behaviour of any lazy field as dynamic at runtime.
- provideViaPropertyBasedProvider: With this configuration feature, you can attach a specific property to a lazy-load condition and enable/disable condition of this property is used to evaluate condition.
For example:
@RowMapperObjectField(
...
lazyLoadCondition =
@RowMapperLazyLoadCondition(
provideViaPropertyBasedProvider =
@RowMapperPropertyBasedLazyLoadConditionProvider(
propertyName = "creditCardInfoLazyLoadCondition")))
private CreditCardInfo creditCardInfo;
To enable/disable lazy-load condition property, there are two ways:
- You can enable/disable lazy-load condition property by using
enableLazyLoadConditionProperty
anddisableLazyLoadConditionProperty
methods oforg.springframework.jdbc.roma.impl.service.RowMapperService
service class.
For example you can get row mapper service class as:
@Autowired
private RowMapperService;
and you can enable lazy-load condition property as:
rowMapperService.enableLazyLoadConditionProperty("creditCardInfoLazyLoadCondition");
or you can disable lazy-load condition property as:
rowMapperService.disableLazyLoadConditionProperty("creditCardInfoLazyLoadCondition");
- You can enable/disable lazy-load condition property automatically by using
@RowMapperPropertyBasedLazyLoadConditionAware
annotation on any method of any class in Spring context before database access code is executed. To enable/disable this property on start of this method and on end of this method can be configured byoptions
property.
For example you can setup lazy-load condition configuration automatically by annotation like:
@Override
@RowMapperPropertyBasedLazyLoadConditionAware(
propertyName = "creditCardInfoLazyLoadCondition",
options = RowMapperPropertyBasedLazyLoadConditionAware.ENABLE_ON_START |
RowMapperPropertyBasedLazyLoadConditionAware.DISABLE_ON_FINISH)
public CreditCardInfo getUserCreditCardInfo(Long userId) {
...
}
- provideViaExpressionBasedProvider: With this configuration feature, you can use RXEL (ROMA Expression Language) expression language to evaluate lazy-load condition.
For example:
@RowMapperObjectField(
...
lazyLoadCondition =
@RowMapperLazyLoadCondition(
provideViaExpressionBasedProvider =
@RowMapperExpressionBasedLazyLoadConditionProvider(
expression = "${name}.equals(\"Member\")")))
private List<Permission> permissions;
- provideViaCustomProvider: With this configuration feature, you can use your custom lazy-load condition evaluater implementations by implementing
org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperLazyLoadCondition.RowMapperLazyLoadConditionProvider
interface for evaluating lazy-load condition as custom. Instance of your implementation is created once and used as singleton.
For example:
@RowMapperObjectField(
...
lazyLoadCondition =
@RowMapperLazyLoadCondition(
provideViaCustomProvider =
@RowMapperCustomLazyLoadConditionProvider(
lazyLoadConditionProvider = UserRolesLazyLoadConditionProvider.class)))
private List<Role> roles;
and your custom lazy-load condition checker implementation is declared as like:
public class UserRolesLazyLoadConditionProvider implements RowMapperLazyLoadConditionProvider<User> {
private final static Logger logger = Logger.getLogger(UserRolesLazyLoadConditionProvider.class);
@Override
public boolean evaluateCondition(User user, String fieldName) {
boolean conditionResult = user.getId() % 2 == 0;
logger.debug("Evaluated lazy-load condition of field " + "\"" + fieldName + "\"" +
" for user with id " + "\"" + user.getId() + "\"" + " as " + "\"" + conditionResult + "\"");
return conditionResult;
}
}
Conditional ignore feature can be configured by using @RowMapperIgnoreCondition
annotation in @RowMapperObjectField
annotation with ignoreCondition
attribute. With conditional ignore feature, you can control turning on/off of mapping behaviour of any field as dynamic at runtime.
- provideViaPropertyBasedProvider: With this configuration feature, you can attach a specific property to a ignore condition and enable/disable condition of this property is used to evaluate condition.
For example:
@RowMapperObjectField(
...
ignoreCondition =
@RowMapperIgnoreCondition(
provideViaPropertyBasedProvider =
@RowMapperPropertyBasedIgnoreConditionProvider(
propertyName = "creditCardInfoIgnoreCondition")))
private CreditCardInfo creditCardInfo;
To enable/disable ignore condition property, there are two ways:
- You can enable/disable ignore condition property by using
enableIgnoreConditionProperty
anddisableIgnoreConditionProperty
methods oforg.springframework.jdbc.roma.impl.service.RowMapperService
service class.
For example you can get row mapper service class as:
@Autowired
private RowMapperService;
and you can enable ignore condition property as:
rowMapperService.enableIgnoreConditionProperty("creditCardInfoIgnoreCondition");
or you can disable ignore condition property as:
rowMapperService.disableIgnoreConditionProperty("creditCardInfoIgnoreCondition");
- You can enable/disable ignore condition property automatically by using
@RowMapperPropertyBasedIgnoreConditionAware
annotation on any method of any class in Spring context before database access code is executed. To enable/disable this property on start of this method and on end of this method can be configured byoptions
property.
For example you can setup ignore condition configuration automatically by annotation like:
@Override
@RowMapperPropertyBasedIgnoreConditionAware(
propertyName = "creditCardInfoIgnoreCondition",
options = RowMapperPropertyBasedIgnoreConditionAware.ENABLE_ON_START |
RowMapperPropertyBasedIgnoreConditionAware.DISABLE_ON_FINISH)
public CreditCardInfo getUserCreditCardInfo(Long userId) {
...
}
- provideViaExpressionBasedProvider: With this configuration feature, you can use RXEL (ROMA Expression Language) expression language to evaluate ignore condition.
For example:
@RowMapperObjectField(
...
ignoreCondition =
@RowMapperIgnoreCondition(
provideViaExpressionBasedProvider =
@RowMapperExpressionBasedIgnoreConditionProvider(
expression = "${name}.equals(\"Member\")")))
private List<Permission> permissions;
- provideViaCustomProvider: With this configuration feature, you can use your custom ignore condition evaluater implementations by implementing
org.springframework.jdbc.roma.api.config.provider.annotation.RowMapperIgnoreCondition.RowMapperIgnoreConditionProvider
interface for evaluating ignore condition as custom. Instance of your implementation is created once and used as singleton.
For example:
@RowMapperObjectField(
...
ignoreCondition =
@RowMapperIgnoreCondition(
provideViaCustomProvider =
@RowMapperCustomIgnoreConditionProvider(
ignoreConditionProvider = UserRolesIgnoreConditionProvider.class)))
private List<Role> roles;
and your custom ignore condition checker implementation is declared as like:
public class UserRolesIgnoreConditionProvider implements RowMapperIgnoreConditionProvider<User> {
private final static Logger logger = Logger.getLogger(UserRolesIgnoreConditionProvider.class);
@Override
public boolean evaluateCondition(User user, String fieldName) {
boolean conditionResult = user.getId() % 2 == 0;
logger.debug("Evaluated ignore condition of field " + "\"" + fieldName + "\"" +
" for user with id " + "\"" + user.getId() + "\"" + " as " + "\"" + conditionResult + "\"");
return conditionResult;
}
}
- Ignoring field: To hide field from row mapper, you can declare this field as
transient
or annotate it with@RowMapperIgnoreField
annotation.
Here is User
class:
@RowMapperClass(objectProcessor = UserObjectProcessor.class)
public class User {
private Long id;
private String username;
private String password;
private String firstname;
private String lastname;
@RowMapperObjectField(
provideViaCustomProvider =
@RowMapperCustomProvider(
fieldProvider = UserPhoneNumberFieldProvider.class))
private String phoneNumber;
@RowMapperObjectField(
provideViaExpressionProvider =
@RowMapperExpressionProvider(
expression = "new Address(&{[string]city}, &{[string]country})",
usedClasses = {Address.class}))
private Address address;
private boolean enabled = true;
private Gender gender;
private Date birthDate;
@RowMapperEnumField(enumStartValue = 1)
private Language language;
@RowMapperEnumField(
mapViaAutoMapper =
@RowMapperEnumAutoMapper(
mapViaNumericValueNumericMappings = {
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 0, value = 0),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 1, value = 100),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 2, value = 200),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 3, value = 300),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 4, value = 400),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 5, value = 500),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 6, value = 600),
@RowMapperEnumNumericValueNumericMapping(mappingIndex = 7, value = 700)
}))
private Occupation occupation;
@RowMapperEnumField(
mapViaAutoMapper =
@RowMapperEnumAutoMapper(
mapViaStringValueNumericMappings = {
@RowMapperEnumStringValueNumericMapping(mappingIndex = 0, value = "PRIMARY_SCHOOL"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 1, value = "SECONDARY_SCHOOL"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 2, value = "HIGH_SCHOOL"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 3, value = "BACHELOR"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 4, value = "MASTER"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 5, value = "PHD"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 6, value = "ASSOCIATE_PROFESSOR"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 7, value = "PROFESSOR"),
@RowMapperEnumStringValueNumericMapping(mappingIndex = 8, value = "OTHER")
}))
private Education education;
@RowMapperEnumField(
mapViaNumericMapper =
@RowMapperEnumNumericMapper(
mapper = BloodTypeEnumMapper.class))
private BloodType bloodType;
@RowMapperEnumField(
mapViaStringMapper =
@RowMapperEnumStringMapper(
mapper = MaritalStatusEnumMapper.class))
private MaritalStatus maritalStatus;
@RowMapperEnumField(useStringValue = true)
private Religion religion;
@RowMapperObjectField(
provideViaExpressionProvider =
@RowMapperExpressionProvider(
expression = "@{roleDAO}.getUserRoleList(${id})"),
lazy = true,
lazyCondition =
@RowMapperLazyCondition(
provideViaCustomProvider =
@RowMapperCustomLazyConditionProvider(
lazyConditionProvider = UserRolesLazyConditionProvider.class)))
private List<Role> roles;
@RowMapperObjectField(
provideViaExpressionProvider =
@RowMapperExpressionProvider(
expression = "@{creditCardInfoDAO}.getUserCreditCardInfo(${id})"),
lazy = true,
lazyCondition =
@RowMapperLazyCondition(
provideViaPropertyBasedProvider =
@RowMapperPropertyBasedLazyConditionProvider(
propertyName = "creditCardInfoLazyCondition")))
private CreditCardInfo creditCardInfo;
@RowMapperObjectField(
provideViaExpressionProvider =
@RowMapperExpressionProvider(
expression = "@{creditCardInfoDAO}.getUserSecondaryCreditCardInfo(${id})"),
lazy = true,
lazyCondition =
@RowMapperLazyCondition(
provideViaPropertyBasedProvider =
@RowMapperPropertyBasedLazyConditionProvider(
propertyName = "creditCardInfoLazyCondition")))
private CreditCardInfo secondaryCreditCardInfo;
@RowMapperObjectField(
provideViaExpressionProvider =
@RowMapperExpressionProvider(
expression = "@{creditCardInfoDAO}.getUserCreditCardInfo(${id})"),
ignoreCondition =
@RowMapperIgnoreCondition(
provideViaPropertyBasedProvider =
@RowMapperPropertyBasedIgnoreConditionProvider(
propertyName = "creditCardInfoIgnoreCondition")))
private CreditCardInfo previousCreditCardInfo;
@RowMapperIgnoreField // Or define field as transient
private byte age;
Here is Role
class:
@RowMapperClass(objectCreater = RoleObjectCreater.class)
public class Role {
private Long id;
@RowMapperField(fieldMapper = RoleNameFieldMapper.class)
private String name;
@RowMapperObjectField(
provideViaSqlProvider =
@RowMapperSqlProvider(
provideSql =
"SELECT p.* FROM PERMISSION p WHERE p.ID IN " +
"(" +
"SELECT rp.PERMISSION_ID FROM role_permission rp WHERE rp.ROLE_ID = ${id}" +
") ORDER BY p.name",
entityType = Permission.class),
lazy = true,
lazyCondition =
@RowMapperLazyCondition(
provideViaExpressionBasedProvider =
@RowMapperExpressionBasedLazyConditionProvider(
expression = "${name}.equals(\"Member\")")))
private List<Permission> permissions;
...
}
Here is BloodTypeEnumMapper
class:
public class BloodTypeEnumMapper implements RowMapperEnumField.NumericEnumMapper<BloodType> {
@Override
public BloodType map(Integer value) {
for (BloodType bt : BloodType.values()) {
if (bt.getCode() == value) {
return bt;
}
}
return null;
}
}
Here is MarialStatusEnumMapper
class:
public class MaritalStatusEnumMapper implements RowMapperEnumField.StringEnumMapper<MaritalStatus> {
@Override
public MaritalStatus map(String value) {
for (MaritalStatus ms : MaritalStatus.values()) {
if (ms.name().equalsIgnoreCase(value)) {
return ms;
}
}
return null;
}
}
Here is UserPhoneNumberFieldProvider
class:
public class UserPhoneNumberFieldProvider implements RowMapperFieldProvider<User, String> {
private final static Logger logger = Logger.getLogger(UserPhoneNumberFieldProvider.class);
@Override
public String provideField(User user, String fieldName, ResultSet rs, int rowNum) {
try {
return rs.getString("phone_number");
}
catch (SQLException e) {
logger.error("Error occured while mapping field " + fieldName + " in User object from resultset", e);
return null;
}
}
}
Here is UserRolesLazyConditionProvider
class:
public class UserRolesLazyConditionProvider implements RowMapperLazyConditionProvider<User> {
private final static Logger logger = Logger.getLogger(UserRolesLazyConditionProvider.class);
@Override
public boolean evaluateCondition(User user, String fieldName, ResultSet rs, int rowNum) {
boolean conditionResult = user.getId() % 2 == 0;
logger.debug("Evaluated lazy condition of field " + "\"" + fieldName + "\"" +
" for user with id " + "\"" + user.getId() + "\"" + " as " + "\"" + conditionResult + "\"");
return conditionResult;
}
}
Here is UserObjectProcessor
class:
public class UserObjectProcessor implements RowMapperObjectProcessor<User> {
@SuppressWarnings("deprecation")
@Override
public void processObject(User user, ResultSet rs, int rowNum) {
if (user.getBirthDate() != null) {
user.setAge((byte)(Calendar.getInstance().getTime().getYear() - user.getBirthDate().getYear()));
}
}
}
Here is RoleObjectCreater
class:
public class RoleObjectCreater implements RowMapperObjectCreater<Role> {
@Override
public Role createObject(Class<Role> clazz) {
return new Role();
}
}
Here is RoleNameFieldMapper
class:
public class RoleNameFieldMapper implements RowMapperFieldMapper<Role> {
private static final Logger logger = Logger.getLogger(RoleNameFieldMapper.class);
@Override
public void mapField(Role role, String fieldName, ResultSet rs, int rowNum) {
try {
role.setName(rs.getString("name"));
}
catch (SQLException e) {
logger.error("Error occured while mapping field " + fieldName +
" in Role object from resultset", e);
}
}
}
Here is UserAccountInfoSqlQueryInfoProvider
class:
public class UserAccountInfoSqlQueryInfoProvider implements RowMapperSqlQueryInfoProvider<User> {
@Override
public SqlQueryInfo provideSqlQueryInfo(User user, String fieldName) {
return
new SqlQueryInfo(
"SELECT a.* FROM ACCOUNT_INFO a WHERE a.ID IN " +
"(" +
"SELECT ua.account_info_id FROM USER_ACCOUNT_INFO ua WHERE ua.user_id = ?" +
")",
new Object[] { user.getId() });
}
}
Here is CreditCardInfoJdbcDAO
class:
@Repository(value="creditCardInfoDAO")
public class CreditCardInfoJdbcDAO extends BaseJdbcDAO implements CreditCardInfoDAO {
...
@Override
@RowMapperPropertyBasedLazyConditionAware(
propertyName = "creditCardInfoLazyCondition",
options = RowMapperPropertyBasedLazyConditionAware.ENABLE_ON_START |
RowMapperPropertyBasedLazyConditionAware.DISABLE_ON_FINISH)
public CreditCardInfo getUserSecondaryCreditCardInfo(Long userId) {
logger.debug("Getting secondary credit card info for user with id " + userId);
return
jdbcTemplate.queryForObject(
"SELECT c.* FROM CREDIT_CARD_INFO c WHERE c.id = " +
"(" +
"SELECT uc.credit_card_info_id FROM USER_SECONDARY_CREDIT_CARD_INFO uc WHERE uc.user_id = " + userId +
")",
creditCardInfoRowMapper);
}
}
You can get User
entity rowmapper as follows:
@Autowired
RowMapperService rowMapperService;
...
RowMapper<User> userRowMapper = rowMapperService.getRowMapper(User.class);
In this example, we can get related Role
entites of User
entity with @RowMapperObjectField
annotion automatically.
We use @RowMapperObjectField
annotation for accessing related Role
entites of User
entity with id
attribute of User.
We have lazy=true
configuration, since roles
field are initialized while we are accessing it first time.
If we don't access it, it will not be set.
You can find all demo codes (including these samples above) at https://github.com/serkan-ozal/spring-jdbc-roma-demo
-
In addition to Annotation based configuration XML and Properties file based configuration support will be added.
-
Spring context xml specific XSD will be defined and configuration can be done in Spring context xml by using tags.
-
Integration with https://github.com/nurkiewicz/spring-data-jdbc-repository which is Spring Data JDBC generic DAO implementation framework by Tomasz Nurkiewicz.
-
Generic DAO implementaion will be added for CRUD operations such as get, list, add, update and delete by considering object relations and different DBMS vendors (Oracle, MySQL, PostgreSQL, ...)