The Unbearable Lightness of Java

Store with Db

The first thing we need to do is to connect to database. As expected, we will initialize database connection in AppCore. But that is not all, we need to set up the DbOom tool in such way to minimize all work with the framework as much as possible. And at the end, we will present a micro database layer that emphasizes simplicity of working with database. So lets starts.

Database initialization

initDb() from AppCore method may look like this:

void initDb() {
	// connection pool
	petite.registerBean("dbpool", CoreConnectionPool.class);
	ConnectionProvider cp = (ConnectionProvider) petite.getBean("dbpool");
	cp.init();

	// global settings
	DbDefault.debug = debugMode;
	DbDefault.connectionProvider = cp;
	DbDefault.sessionProvider = new ThreadDbSessionProvider(true);

	DbOomManager dbOomManager = DbOomManager.getInstance();

	// manual configuration (before entities registration)
	dbOomManager.setTableNamePrefix("wm_");

	// automatic configuration
	AutomagicDbOomConfigurator dbcfg = new AutomagicDbOomConfigurator();
	dbcfg.setIncludedEntries(new String[] {this.getClass().getPackage().getName() + ".*"});
	dbcfg.configure(dbOomManager);
}

Connection pool

We will use CoreConnectionPool as application connection pool. It is working, it is simple and it is available out from Tomcat. Of course, nothing is stopping us from using DataSource - in that case we would use the DataSourceConnectionProvider instead, however, data source is not available out from Tomcat, then usually you need to put jdbc driver in Tomcats lib folder, and, finally, we need to set datasource from our web application layer.

Why connection pool is registered in Petite? Again, it is not necessary - we did it so in order to configure it out from application properties files (that were loaded during Petite initialization). So our properties may contain the following:

app.debugMode=true

# database
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/somename
jdbc.username=root
jdbc.password=root!

# db pool
dbpool.driver=$jdbc.driverClassName
dbpool.url=$jdbc.url
dbpool.user=$jdbc.username
dbpool.password=$jdbc.password
dbpool.maxConnections=50
dbpool.minConnections=5
dbpool.waitIfBusy=true

SessionProvider

Since we do not want to manually open sessions, we can setup the session provider: nice friendly class that will return session when required. Since we are building a web application, we can assume that each request is a separate thread, so we can create a db session and store it in the thread storage to make it available during the request. This is set in lines 9 and 10. When ThreadSessionProvider is created with true flag, it will be create db sessions if missing (otherwise it throws an exception).

DbOom configuration

DbOom is set through DbOomManager. The only thing we set here is table prefix. Then we scan the classpath for all entities and register then automatically.

Entities

Although db entities in Jodd are POJOs, it will be healthy to create one base abstract type for all entities. To make our life simpler, we gonna assume more: lets say that all entities has Long primary key named ID. Now, if id property of entity is set, we say that entity is persistent (stored in database), if property is null, we say entity is new, not saved yet. So our abstract entity may look like:

public abstract class Entity {

	@DbId
	protected Long id;
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}

	public boolean isPersistent() {
		return id != null;
	}

	@Override
	public int hashCode() {
		if (id == null) {
			return System.identityHashCode(this);
		}
		return 31 * id.hashCode();
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o instanceof Entity == false) {
			return false;
		}
		Entity entity = (Entity) o;
		if (id == null && entity.id == null) {
			return this == o;
		}
		if ((id == null) || (entity.id == null)) {
			return false;
		}
		return id.equals(entity.id);
	}

	@Override
	public String toString() {
		return "Entity{" + this.getClass().getSimpleName() + ':' + id +	'}';
	}
}

Domain objects inherits the Entity:

@DbTable
public class User extends Entity {

	@DbColumn
	private String fullName;

	@DbColumn
	private String email;

	@DbColumn
	private String hashpw;

	...
	// accessors methods
}

Generic Dao

Now we are able to create generic DAO for basic operations. We will use DbEntitySql generators for that. Before we continue, we will assume one last thing: the IDs are autogenerated on insert. Again, we can build the generic DAO in other cases as well, such as using sequences for calculating the next ID value or using specific tables etc; this all depends on how You would like to organize the database.

Back to generic DAO source:

@PetiteBean
public class AppDao {

	public <E extends Entity> E store(E entity) {
		if (entity.isPersistent() == false) {
			DbQuery q = query(insert(entity));
			q.setGeneratedColumns();
			q.executeUpdate();
			long key = q.getGeneratedKey();
			entity.setId(Long.valueOf(key));
			q.close();
		} else {
			query(updateAll(entity)).executeUpdateAndClose();
		}
		return entity;
	}

	public <E extends Entity> E findById(Class<E> entityType, Long id) {
		return query(DbEntitySql.findById(entityType, id)).findOneAndClose(entityType);
	}

	public <E extends Entity> E findOne(E criteria) {
		return (E) query(DbEntitySql.find(criteria)).findOneAndClose(criteria.getClass());
	}

	public <E extends Entity> List<E> find(E criteria) {
		return (List<E>) query(DbEntitySql.find(criteria)).listOneAndClose(criteria.getClass());
	}

	public void deleteById(Class entityType, Long id) {
		query(DbEntitySql.deleteById(entityType, id)).executeUpdateAndClose();
	}

....

}

Notice there is no single line of SQL used in the code.

Usage example

From here you can continue in any direction you like. Here is just a quick usage example. First, lets wire AppDao with FooService:

@PetiteBean
public class FooService {

	@PetiteInject
	AppDao appDao;

	public void storeUser(User user) {
		appDao.store(user);
	}
}

Here is how we can use it from our LocalRunner:

public class LocalRunner {

	public static void main(String[] args) {
		AppCore appCore = new AppCore();
		appCore.start();

		FooService fooService = (FooService) appCore.getBean("fooService");

		User user = new User();
		user.setFullName("test");
		user.setHashpw("...");
		user.setEmail("xxxx");
		fooService.storeUser(user);		// insert new user
		System.out.println(user);
		user.setEmail("****");
		fooService.storeUser(user);		// update user

		ThreadDbSessionProvider.closeDbSession();
		appCore.stop();
	}
}

Database session is created in line #13 and assigned to current thread. In line #16, session is reused from thread storage. At the end, we must close session explicitly, in line #18.