Most applications nowadays are using relational databases as the primary means to store and query their business data. Since entities are used as an abstraction for business (domain) data and behaviour its obvious to provide some mechanism to map these entities to a persistent database representation and to provide some querying facilities. This is what the repository model element is used for. Up to now the generator only supports hibernate based repository implementations and expects the repository declaration to be in the same model (file) as the corresponding entity.
A repository element starts with the repository keyword followed by a name and a reference to the entity this provider is mapped to.The following example shows a fairly complete example of the currently supported repository mapping elements and how they relate to the referenced entity. We will refer to this example when we are going to explain each of the currently possible elements in the following sections.

Note: For the sake of completness he previous figure shows all possible features of repository elements. Many of them are optional or automatically derived from a namingstrategy or from some information already available in the entity corresponding element and doesnt have to be explicitly defined.
A repository element accepts the following attributes.
a required for [Entity]
specifies the entity this repository is used for
an optional table specifies the name of its
database table
Note: this is only required if the name provided from the configured NamingStrategy has to be overriden
an optional qualifier specifies a string that
distinguishes individual subclasses in a polymorphic
relationship
Note: A qualifier value maps to the discriminator-value from the table-per-class inheritance mapping stratgey of hibernate. Right now this is the only supported inheritance mapping strategy.
an optional discriminator specifies the name of the
column which holds the qualifier value
Operations come in two flavours. The first one is only used to create the signature and a placeholder for data access code which must be manually implemented as shown in the listing below. Manual operations are used in all cases where QL operations (shown next) are not sufficient enough.
operation CustomerContactReport[] loadCustomerReport(String name)The declaration of the operation shown above generates the following signature and empty method body which has to be implemented manually.
Note: The generation of an
empty method body in the manual CustomerDaoImpl actually
happens only the first time if the file doesn't exist yet.
// in CustomerDaoGen
Collection<CustomerContactReport> loadCustomerReport(String name);
// in CustomerDaoImpl
public Collection<CustomerContactReport> loadCustomerReport(String name) {
return null;
}Query operations look alike manual operations with the additional specification of a hibernate query language (and the subset standardized as JPA QL) statement. A particualar advantage of this kind of operation is the instant syntax verification and content assist for the given query string and parameters.
operation Customer[] findAllByFirstNameAndLastName(String firstName,String lastName) :
from Customer c
where
c.firstName like :firstName and
c.lastName like :lastNameThe declaration of this query operation creates the following code within the generated DaoGenImpl class.
public Collection<Customer> findAllByFirstNameAndLastName(String firstName,String lastName) {
Query namedQuery = this.sessionFactory.getCurrentSession().getNamedQuery("Customer.FindAllByFirstNameAndLastName");
namedQuery.setParameter("firstName", firstName);
namedQuery.setParameter("lastName", lastName);
applyFindAllByFirstNameAndLastNameQueryHints(namedQuery,firstName,lastName);
return list(namedQuery);
}
protected void applyFindAllByFirstNameAndLastNameQueryHints(Query query,String firstName,String lastName) {
// override this method and apply other hints that influence how a query is executed
}The query statement gets generated as an externalized string into the mapping configuration file of the corresponding entity. The name of the query in the mapping document gets derived from the entity and operation name.
<query name="Customer.FindAllByFirstNameAndLastName">
<![CDATA[
from CustomerImpl as c
where c.firstName like :firstName and c.lastName like :lastName
]]>
</query>We support the following kinds of query statements:
delete statements
operation /* optional Integer */ deleteAllCustomersLikeName(lastName) :
delete from Customer as customer
where customer.lastName = :lastName
update statements
operation updateCustomer(String newName,String oldName) :
update Customer customer set
customer.name = :newName
where customer.name = :oldName
// according to the EJB3 specification, HQL UPDATE statements, by default, do not effect the version or
// the timestamp property values for the affected entities..
// .. force Hibernate to reset the version or timestamp property values through the use of a versioned update
operation updateCustomerVersioned(String newName,String oldName) :
update versioned Customer customer set
customer.name = :newName
where customer.name = :oldNameinsert statements
insert into Customer ( firstName , lastName )
select
customer.firstName,
customer.lastName
from Customer customer
where
customer.clientId = :clientIdselect statements returning entities or scalar values
operation Customer[] findAllByFirstNameAndLastName(String firstName,String lastName) :
from Customer c
where
c.firstName like :firstName and
c.lastName like :lastName
operation Customer findCustomerByOid(oid) :
from Customer as customer
where
customer.oid = :oid
operation String[] getAllCustomerNames() :
select
distinct customer.lastName
from Customer customer
select statements returning projected dataviews
dataview CustomerNameReport {
Integer groupCount
Customer.lastName
}
.
.
operation CustomerNameReport[] loadCustomerNameReport() :
select
customer.lastName,
count(customer) as groupCount
from Customer customer
group by customer.lastNameThe last select statement is a so called report query which let you specify which attributes to retrieve . Since report queries doesnt returned managed entity instances but only data tuples the generator needs to create some additional mapping code to map from those tuples to the specified query return type like the following one.
protected CustomerNameReport mapLoadCustomerNameReportTuple(Object[] tuple, int rowNumber) {
CustomerNameReport customerNameReport = new CustomerNameReport();
customerNameReport.setLastName((String)tuple[0]);
customerNameReport.setGroupCount((Integer)tuple[1]);
return customerNameReport;
}Note: This pattern supports the recommendation from the hibernate best practice chapter given below:
"Consider externalizing query strings: This is recommended if your queries call non-ANSI-standard SQL functions. Externalizing the query strings to mapping files will make the application more portable."
Callable Statement operations support the execution of Sql Stored Procedures and Functions. They allow for the specification of IN and OUT (IN-OUT) parameters combined with the mapping of complex structures passed into or returned from the declaring operation as shown in the examples below. The general structure of callable statement operations is outlined in the following figure.

The following section gives an overview of the currently supported options of callable statement usage
basic call with and without package name
repository CustomerDao for Customer {
operation spWithoutPackageName() :
call sp_foo()
operation spWithFooPackageName() :
call FOOPACKAGE.sp_foo()
}stored procedure call without any input- or output parameter
repository CustomerDao for Customer {
operation spWithoutParameters() :
call sp_foo()
}sp call with single input parameter and no output parameter
repository CustomerDao for Customer {
operation spWithParameters(String name) :
call sp_foo(name)
}with single input parameter and name aliasing (and no output parameter)
repository CustomerDao for Customer {
operation spWithNameAliasing(String name) :
call sp_foo(name as IN_NAME)
}with multiple input parameters with and without name aliasing
repository CustomerDao for Customer {
operation spWithMultiInParameter(String name,Date fromDate,Integer maxLimit) :
call sp_foo(name as IN_NAME,fromDate as IN_DATE,maxLimit)
}with complex type input parameter
repository CustomerDao for Customer {
operation spWithComplexTypeInParameter(FooInput fooInputParameter) :
call sp_foo(fooInputParameter.name as IN_NAME,fooInputParameter.fromDate as IN_DATE,fooInputParameter.maxLimit)
}
dataview FooInput {
String name
Date fromDate
Integer maxLimit
}function call with scalar return type
repository CustomerDao for Customer {
operation Integer functionWithScalarReturnType(Date fromDate,Date toDate) :
call function foo_function(fromDate,toDate)
}function call with scalar return type and explicit output parameter mapping
repository CustomerDao for Customer {
operation Integer functionWithExcplicitOutParameterMapping(Date fromDate,Date toDate) :
call function foo_function(fromDate,toDate)
return OUT_COUNT
}stored procedure call which returns a collection of some scalar return type
repository CustomerDao for Customer {
operation Integer[] spWithScalarCollectionReturn(Date fromDate,Date toDate) :
call get_customer_ids(fromDate,toDate)
return CUSTOMER_OID
}stored procedure call with complex type output mapping
repository CustomerDao for Customer {
operation FooOutput spWithComplexTypeOutputMapping(Date fromDate,Date toDate) :
call get_customer_ids(fromDate,toDate)
return rowCount,
created,
name
}
dataview FooOutput {
Integer rowCount
Date created
String name
}stored procedure call with complex type output mapping and out parameter name aliasing
repository CustomerDao for Customer {
operation FooOutput spWithComplexTypeOutputMapping(Date fromDate,Date toDate) :
call get_customer_ids(fromDate,toDate)
return OUT_ROW_COUNT as rowCount,
OUT_DATE_CREATED as created,
OUT_CUSTOMER_NAME as name
}
dataview FooOutput {
Integer rowCount
Date created
String name
}stored procedure call with complex type collection output mapping
repository CustomerDao for Customer {
operation FooResult[] spWithComplexTypeOutputMapping(Date fromDate,Date toDate) :
call get_customer_ids(fromDate,toDate)
return created,
name
}
dataview FooResult {
Date created
String name
}stored procedure call with nested complex type collection output mapping
repository CustomerDao for Customer {
operation FooOutput spWithComplexTypeOutputMapping(Date fromDate,Date toDate) :
call get_customer_ids(fromDate,toDate)
return rowCount,
results.created,
results.name
}
dataview FooOutput {
Integer rowCount
FooOutput[] results
}
dataview FooResult {
Date created
String name
}stored procedure call with nested complex type output mapping
repository CustomerDao for Customer {
operation FooOutput spWithComplexTypeOutputMapping(Date fromDate,Date toDate) :
call get_customer_ids(fromDate,toDate)
return rowCount,
result.created,
result.name
}
dataview FooOutput {
Integer rowCount
FooOutput result
}
dataview FooResult {
Date created
String name
}The generated body of an callable statement operation uses spring
SimpleJdbcCall abstraction for the actual stored procedure
or function call. Those operations are further split up into three
protected subcalls which allow for better customization and
organization of the generated code. The following section lists the
generated subcalls together with an example and short explanation of
each
prepare%OPERATION_NAME%JdbcCall
protected SimpleJdbcCall prepareSpWithComplexTypeOutputMappingJdbcCall(Date fromDate,Date toDate) {
SimpleJdbcCall jdbcCall = createJdbcCall(SP_SP_WITH_COMPLEX_TYPE_OUTPUT_MAPPING);
return jdbcCall;
}The purpose of prepare%OPERATION_NAME%JdbcCall
is to simply create and setup the SimpleJdbcCall
instance of the actual stored procedure or function call. By
default this operation returns a new SimpleJdbcCall
instance for every invocation. Since SimpleJdbcCallis
designed as reusable (multi-threaded) object an overriden,
customized implementation of
prepare%OPERATION_NAME%JdbcCall could for example
return a cached SimpleJdbcCall instance for repeated
invocations. (or mock for unit tests) Another performance related
optimization version could turn-off the default meta data
processing of SimpleJdbcCall and explicitly declare
all input- and output parameter names together with their matching
Sql type as shown in the following example. Also, if you need to
pass any database-vendor specific types (e.g. oracles Array
datatype) you probably have to override this implementation
too.
@Override
protected SimpleJdbcCall prepareSpWithComplexTypeOutputMappingJdbcCall(Date fromDate, Date toDate)
if (this.jdbcCall == null) {
jdbcCall = super.prepareSpWithComplexTypeOutputMappingJdbcCall(fromDate, toDate);
jdbcCall.withoutProcedureColumnMetaDataAccess()
.declareParameters(new SqlParameter("IN_FROM_DATE", Types.DATE))
.declareParameters(new SqlParameter("IN_TO_DATE", Types.DATE))
.declareParameters(new SqlOutParameter("OUT_FIRST_NAME", Types.VARCHAR));
}
return jdbcCall;
}execute%OPERATION_NAME%
protected Map<String, Object> executeSpWithComplexTypeOutputMapping(SimpleJdbcCall jdbcCall,Date fromDate,Date toDate) {
Map<String,Object> parametersMap = new HashMap<String,Object>();
parametersMap.put("fromDate",fromDate);
parametersMap.put("toDate",toDate);
return executeJdbcCall(jdbcCall,parametersMap);
}Uses the previously created SimpleJdbcCall
instance to execute the jdbc call with a map of configured
parameter names and values. Override this method to provide
default parameters or values. (override #executeJdbcCall to handle
all jdbc calls of a given repository)
map%OPERATION_NAME%Result
protected FooOutput mapSpWithComplexTypeOutputMappingResult(Map<String, Object> _resultMap,Date fromDate,Date toDate) {
FooOutput _fooOutput = new FooOutput();
_fooOutput.setRowCount((Integer)_resultMap.get("rowCount"));
FooResult _result = new FooResult();
_fooOutput.setResult(_result);
_result.setCreated((Date)_resultMap.get("DAT_CREATED"));
_result.setName((String)_resultMap.get("NAM_LAST_NAME"));
return _fooOutput;
}Only for callable statements with a declared return type. Receives the result of a preceeding jdbc call invocation as generic map structure and converts from map entries to the specified return type based on the output parameter mapping of the corresponding return statement.
Columns map attributes to respective columns and only have to be explicitly specified if
the calculated column name from the configured (or default) naming strategy does not match (and you don't want to implement a custom naming strategy)
entity Customer extends BaseEntity {
String ssn
}
repository CustomerDao for Customer {
column ssn <-> "SOCIAL_SECURITY_NUMBER" // instead of SSN
}the default column or hibernate type must be overriden or no sensible defaults can be infered automatically like in the following example as the newletter boolean has to be mapped with a special usertype which persists the boolean value as y/n characters.
Note: there is also another possibility with the openXMA generator preferences within eclipse to map usertype's to attribute types within a workspace or project
entity Customer extends BaseEntity {
Boolean newsletter
}
repository CustomerDao for Customer {
column newsletter usertype=YesNoType
}For the first case there are actually two ways how to map
attributes to column names. One can either explicitly declare the
attribute mapping with the column keyword (as already shown
above) or adapt and configure the namingstrategy which the generator
will use to provide the name in the automatically created column
elements.
Columns also support the nested mapping of multi-valued attribute types like Address or Money like in the following mapping scenario.
valueobject Money {
String currency
Integer amount
}
entity Customer extends BaseEntity {
Money money
}
repository CustomerDao for Customer {
column money {
column currency <-> "MONEY_CURRENCY"
column amount <-> "MONEY_AMOUNT"
}
}Used to map an ordinary reference or containment (cardinality 1 or 0) defined in the enclosing entity to a foreign-key (constrained) column of another table (entity).
repository CustomerDao for Customer {
many-to-one invoiceAddress <-> "ID_INV_ADRE"
}Many-To-One elements support the following optional attributes:
<-> the name of the foreign-key column
Usually it's not required to explicitly declare this
many-to-one element (besides to explicitly override and set
the above mentioned properties) because it (i.e. the table which holds
the foreign key) can always be derrived from the one-ended reference end
as shown in the next example.
entity Customer extends BaseEntity {
String firstName
String lastName
String ssn
Boolean premiumMember
Address address
}
entity Address extends BaseEntity {
String(30) streetName
String streetNumber
String(10) zip
Customer customer oppositeof address
}In this bi-directional Customer - Address
relation the generator can derive the many-to-one side from
the cardinality info (zero or many address vs. zero or one customer) of
the two involved references. In a uni- or bidirectional one-to-one
relation the following rules are applied to derive the many-to-one
side
specification of an opposite reference which is either marked
as containment or multi-valued (i.e. collection) and
which isnt required
the reference is marked as required
existence of an explicit many-to-one element
within the associated provider element
the reference is a unidirectional reference (e.g.
unidirectional one-to-one association on a foreign
key)
Used to map a collection-valued (with list or set collection type) reference or containment to as persistent relation.
provider CustomerDao for Customer {
one-to-many orders <-> "ID_CUST"
}One-To-Many elements support the following optional attributes.
<-> the name of the (opposite) foreign-key
column
As already stated in the previous section about
many-to-one mappings it is not necessary to explicitly
declare this one-to-many element since the generator can derive this
information from the 'oppositeof' reference declaration.
Used to map the opposite role in a bi-directional one-to-one association. This is the opposite end of the association containing the foreign-key (many-to-one) as shown in the following example. The only reason up to now to declare this mapping element expliciltly is to manually set the cascade behaviour. For all other bi-directional one-to-one (to one-to-many) association mapping its NOT required to declare this element since the generator can infer it automatically. By default the generator infers the 'one-to-one side' from the opposite reference declaration of a bi-directional relationship like so.
entity Customer extends BaseEntity {
String firstName
String lastName
String ssn
Boolean premiumMember
ref Address address
}
entity Address extends BaseEntity {
String(30) streetName
String streetNumber
String(10) zip
Customer customer oppositeof address
}Or to put it in another way the one reference WITHOUT an existing
opposite declaration (address) is always assumed to be the
many-to-one side (i.e. the table which holds the foreign key) within a
bi-directional one-to-many or one-to-one relation. Because of this
rather limited usage we are currently evaluating to deprecate or remove
this element from the dsl.
For each repository element two or more java classes and three hibernate mapping files are generated. The first java file is the DaoGen interface which extends a generic Dao interface from the platform library and provides several ready to use factory and crud methods. One factory method without parameter and an additional factory method taking all required attributes and (to-one) references to provide a consistent way of entity construction. The second java file represents the actual hibernate based implementation which implements the generated interface.
The three hibernate mapping files are denominated with three different suffixes:
".hbm.xml": the actual merged mapping file used at runtime (location: project folder for generated resources ("src-gen" or "src/generated/resources"))
".hbm.fragment.xml": the mapping file used to configure hibernate mapping customisation (location: project folder for source resources ("src" or "src/main/resources"))
".hbm.gen.xml": the raw generated mapping file, without merged in customisations, derived from the entity model only (location: project folder for generated resources ("src-gen" or "src/generated/resources"))
This empowers the developer to further customise the generated hibernate mapping file used at runtime, which would otherwise not be possible only by the means of the entity model. The reason why is, that hibernate offers a wide array of configuration options and it dos not make sense to abstract this on a model level. The hbm.gen.xml file should not be used at runtime as it does not reflect the customisations made in the fragment file. It merely works as a reference for the raw generated hibernate mapping file content. It will be generated every time the generator workflow is started, like the hbm.xml file.
After the generator workflow has generated the hbm.xml and hbm.gen file, it searches for a fragment file. If it found none, it will create one. If it found one, it will check, if there are elements to be merged into the hbm.xml file, keeping the hbm.gen file untouched. Therefore the developer has to be aware of his or her customisations made in the fragment file, as they overrule the mapping elements in the generated hbm.xml file.
Note: The generation of a separate mapping configuraton document for each entity supports the recommendation from the hibernate best practice chapter given below:
"Place each class mapping in its own file: Do not use a single monolithic mapping document. Map com.eg.Foo in the file com/eg/Foo.hbm.xml. This makes sense, particularly in a team environment."