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 DbOrm 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.
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);
DbOrmManager dbOrmManager = DbOrmManager.getInstance();
// manual configuration (before entities registration)
dbOrmManager.setTableNamePrefix("wm_");
// automatic configuration
AutomagicDbOrmConfigurator dbcfg = new AutomagicDbOrmConfigurator();
dbcfg.setIncludedEntries(new String[] {this.getClass().getPackage().getName() + ".*"});
dbcfg.configure(dbOrmManager);
}
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
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).
DbOrm is set through DbOrmManager. The only thing we set here is table prefix. Then we scan the classpath for all entities and register then automatically.
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
}
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.
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.