CUBA Database Access


DataSources

This chapter explains basic access to SQL databases by means of DataSources which can be looked up from a component or client context and allow to obtain a database connection. The connection can be used to execute plain JDBC calls or to feed additional persistence managers. Adapters for the open source O/R mappers Hibernate, PriDE, and OJB are available from the CUBA website and are part of the examples of the CUBA delivery. Another convenient and standardized way to work with SQL databases is the use of the Java Persistence API which is alos supported by CUBA (siehe chapter JPA). However, the concept of DataSources is the common basis for all persistence management techniques. Transaction management is separated from the connection management and direct transaction management calls on a connection object are forbidden. The details of connection and transaction management concepts of the EJB standard will not be discussed here but can be found in the EJB specification and in related literature. We will focus on the database-related functions of the CUBA API and the configuration aspects required for the embedded container.

The following code shows how to obtain a database connection from a global lookup:
 
public class MyComponentImpl
  extends AbstractComponent implements MyComponent {

  public void operationWithDB() throws SQLException {
    ClientContextI cc = getContext().getClientContext();
    DataSource ds = cc.getDataSource("myDB");
    Connection con = ds.getConnection();
    //do something useful
    con.close();
  }
}

In an EJB system, the lookup above only requires the managed database "myDB" to be configured as a DataSource in the application server, just as usual. Using the component in wired mode requires a corresponding declaration in the application descriptor wired-application.xml:
 
<wired-application>
  <display-name>DB application</dispply-name>
  <modules>
    <wired>mycomponent.jar</wired>
  </modules>
  <resources>
    <data-source resource-name="myDB">
      <url>...</url>
      <driver>...</driver>
      <user>...</user>
      <password>...</password>
    </data-source>
  </resources>
</wired-application>

The referenced JDBC driver does not have to be XA-capable because CUBA's wired container does not perform a 2-phase-commit protocol even when accessing multiple databases. If a feature like this is required, it is strongly recommended to run the components as EJBs and use the powerful transaction management capabilities of an application server. For simple applications and for component testing, a simple commit is sufficient. The elements user and password are optional.

Connection Pooling

The J2EE-style of connection management usually makes it essential to establish connection pools which the container can allocate connections from very fastly whenever requested by a component. As this is also true for CUBA's embedded container, connection pooling can be declared in the wired-application.xml too. Even if you run a single-threaded stand-alone application, it is often reasonable to establish a pool of size 1 to allocate a connection only once in the application's lifetime and keep it allocated for fast repeated usage. Connection pooling is declared as an optional element in a DataSource declaration as follows:
 
<data-source resource-name="myDB">
  <url>...</url>
  <driver>...</driver>
  <user>...</user>
  <password>...</password>
  <connection-pool min-size="1" max-size="10"/>
</data-source>

Alternative tag sources

By default, the tag values for a DataSource declaration are taken as specified in the wired-application.xml which is sometimes too static. If you require more flexibility without wanting to modify the descriptor file, you can use the source attribute for an indirect specification, e.g.

<data-source resource-name="myDB">
  <url source="system">db.url</url>
  <driver source="system">db.driver</driver>
</data-source>

The value "system" causes the specified value to be interpreted as the name of a system property which to extract the actual value from. I.e. starting a programm with a descriptor as above would allow to specify the actual database URL by command line option "-Ddb.url=my-funny-url". Another alternative is to use the value "init" for the source-attribute, to refer to the property list passed to the constructor of the WiredClientContext. This feature is available for all descriptor tags, allowing not only to flexibelize DataSource declarations but also the component linkage - e.g. for the integration of mock components during testing.

Transactions

Transactions can be managed by the container or by components in CUBA. Similar to the EJB standard, container-managed transactions are declared on method level in the component's deployment descriptor or by Java code annotations. The following code is an example of such a declaration in a descriptor, setting transaction attribute Required for all methods of a component. Methods can be addressed by name and by wildcard but not by method signature as in an EJB deployment descriptor (which is a pretty questionable technique anyway).
 
<component-jar>
  <component>
    ...
    <component-name>hello</component-name>
    <component-class>hello.HelloWorldImpl</component-class>
    <transaction type="Container">
      <container-transaction attribute="Required" method-name="*"/>
    </transaction>
  </component>
</component-jar>

As an alternative, the components can mark transaction boundaries themselves, using an interface similar to UserTransaction in J2EE. When working in an EJB environment, this is only a compatibility interface over javax.transaction.UserTransaction.
 
public class MyComponentImpl
  extends AbstractComponent implements MyComponentContractI {

  public void operationWithTX()
    throws SQLException, TransactionException {
    ClientContextI cc = getContext().getClientContext();
    DataSourceI ds = cc.getDataSource("myDB");
    Connection con = ds.getConnection();

    TransactionI trans = getContext().getTransaction();
    trans.begin();
    //do something useful
    trans.commit();
  }
}

Global vs. Local Lookup

The lookup of DataSources can be performed either globally or locally which is a major difference for J2EE environments (see the EJB specification for details). The examples above perform only global lookups, by making a client context out of a component context and looking up the DataSource from the client context. This approach ist less flexible, but it is very common when integrating additional persistence management frameworks. Client context lookups are always global lookups while component context lookups are always local. I.e. they do not refer to a global resource name space directly, but a component's local lookup tables. These local name spaces are much faster to access in an EJB container. Using a local lookup, the first example of this chapter becomes a little simpler:
 
public class MyComponentImpl
  extends AbstractComponent implements MyComponent {

  public void operationWithDB() throws SQLException {
    DataSourceI ds = getContext().getDataSource("myDBref");
    Connection con = ds.getConnection();
    //do something useful
    con.close();
  }
}

However, the local reference name "myDBref" must be declared in the component's deployment descriptor and mapped to a globally defined DataSource. A reference declaration looks like this in a component-jar.xml:
 
<component-jar>
  <component>
    <component-name>myComp</component-name>
    ...
    <resource-refs>
      <resource-ref ref-name="myDBref" interface="javax.sql.DataSource"/>
    </resource-refs>
  </component>
</component-jar>

For a wired application, the mapping is done in the reference section in wired-application.xml, e.g.
 
<references>
  <component-references component-name="hello">
    <resource-refs>
      <resource-ref ref-name="myDBref" refers-global-name="myDB"/>
    </resource-refs>
  </component-references>
</references>

In the EJB standard, the exact mechanism of these mappings is not defined and therefore is solved differently by every application server vendor. This is why they are part of the vendor-specific deployment descriptors which are not generated by CUBA but must be added separately, either by hand-coding or by interactive deployment tools. This is a nasty weakness of the standard.
To put it in a nutshell, this technique seems to be more complicated at first sight. But using one apprach or the other is usually not a matter of convenience but of requirements.

An example for database access with CUBA is available under examples/database.


Home Introduction Javadoc