Flexible persistency – SQL or NoSQL

The reference application that I am building, supports both relational database or document database persistency.

The object model of my application consists of classes that are used in both. A factory determines dynamically whether to use a SQL or a NO-SQL database.  Or to be more precise, MySQL or MongoDB.

Java EE JPA is used to make it fit almost any SQL database, while MongoDB specific API is used to operate on MongoDB. Alltough JPA stands for Java Persistency API and is therefore not necessarily related to SQL databases, the use of specific annotations more or less assume that SQL databases are used.

In my application the domain objects do have JPA annotations and are used by an entity manager for persistence in MySQL and a bespoke document manager for MongoDB. All implementing the same interface.

The interface looks like:

@Local
public interface BankAccountManager {

	
	public List<BankAccount> getBankAccounts(User user);
	public List<MoneyTransfer> getMutations(BankAccount bank);
	public void storeMutations(BankAccount bAccount,List<MoneyTransfer> transfers);
	
}

The factory for getting the right implementation looks like:

@Singleton
public class BankAccountManagerFactory {

	@EJB(beanName="BankAccountDocumentManager")
    private BankAccountManager docManager;
    
	@EJB(beanName="BankAccountEntityManager")
    private BankAccountManager entityManager;
    
	private BankAccountManager expManager;
	
	private boolean isDoc = false;      
	
	@PostConstruct
	public void initEM() {
		if (isDoc) {
			expManager = docManager;
		} else {
			expManager = entityManager;
		}
	}
    
	public BankAccountManager getBankAccountManager() {
		return expManager;
	}
	
}

In this example, the choice for using one or the other is hardcoded, but merely used as an example that you can switch based on something in your code path. The names of the EJB’s will be matched to the correct implementations of the EJB’s for this same interface. The names must be specified otherwise the correct implementation cannot be determined by your container.

A sample class that uses the BankAccountManager then looks like:

@WebServlet("/upload")
@MultipartConfig
public class UploadSwift extends HttpServlet {
	
	private static final long serialVersionUID = 1L;
	@EJB private BankAccountManagerFactory bankAccountManager;

So in my application I could have chosen to use @EJB(beanName=”BankAccountEntityManager”) in my servlet, but that would make it really hardcoded. Using the factory I can determine it by some other rule. It’s still a hardcoded boolean at the moment, but could also be an environment variable or such thing.

In a future post, I will explain more about the differences in schema design and differences between relational and document oriented data.

Database independent JPA

Java EE 6 contains the JPA 2.0 specification. This means that applications that are built using this interface can be run using application servers that have their own implementation of the JPA 2.0.

Using JPA 2.0 in a Maven enabled Java project would require only this single dependency at provided scope:

<dependency>
	<groupId>javax</groupId>
	<artifactId>javaee-api</artifactId>
	<version>6.0</version>
	<scope>provided</scope>
</dependency>

Does this also mean, that your code can run on any database? No, it does not. It depends for instance on the capabilities of your database. A simple example is the use of auto generated numbers for primary keys. Oracle 11g does not support this, while Derby and MySQL databases do understand this.

The following code therefore, is only applicable to databases that understand and support the concept:

@Entity(name="users")
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @XmlTransient
    @Column(name="user_id")
    private int id;

Oracle 11g users may need to use values taken from sequences:

@Entity(name="users") 
public class User { 

     @Id 
     @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="USER_SEQ")        
     @SequenceGenerator(name="USER_SEQ", sequenceName="USER_SEQ", allocationSize=10) 
     @XmlTransient 
     @Column(name="user_id") 
     private int id; 

Again, this is not a bad thing. Using the capabilites of a particular database has real benefit. However, it is wise to keep this in mind while developing and testing your solution. Once you start using functionality of a database that goes beyond simple SQL operations, you really should have such a database available in your development and test environment.