How can I reduce code duplication in my database access layer? - java

I am new to Java and I'm trying to implement a basic database access layer.
I'm using Apache DBUtils to reduce JDBC boilerplate code and this is working really well.
The problem is that my implementation uses a separate class for CRUD for each table in my database and it feels wrong to be duplicating so much functionality.
Is this an acceptable design and if not what can I do to reduce code duplication?
Could I refactor my solution to use generics in some fashion?
I realize I could use an ORM (myBatis, Hibernate etc) as a solution but I would like to try to stick with DBUtils and plain JDBC if I can help it.
Just for clarification:
Lets say I have 2 tables...
---------------------
User | File
---------------------
userId | fileId
name | path
age | size
---------------------
In my current solution I would create 2 classes (UserStore, FileStore) and
each class would implement similar basic CRUD methods:
protected boolean Create(User newUser)
{
QueryRunner run = new QueryRunner(dataSource);
try
{
run.update("INSERT INTO User (name, age) " +
"VALUES (?, ?)", newUser.getName(), newUser.getAge());
}
catch (SQLException ex)
{
Log.logException(ex);
return false;
}
return true;
}
protected User Read(int userId)
{
try
{
User user = run.query("SELECT * FROM User WHERE userId = ? ", userId);
return user;
}
catch (SQLException ex)
{
Log.logException(ex);
return null;
}
}
protected update(User user)
{
... perform database query etc
}
protected delete(int userId)
{
... perform database query etc
}

You asked how i would do this with Template method. Here is an example how you could do it:
public class AbstractDAO<T> {
private String table;
private String id_field;
public AbstractDAO(String table, String id_field){
this.table = table;
...
}
public T read(int id){
try
{
T user = run.query("SELECT * FROM "+ table + " WHERE "+id_field +" = ? ", id);
return user;
}
catch (SQLException ex)
{
Log.logException(ex);
return null;
}
}
This one looks easy, how about Create?
public boolean Create(T user){
QueryRunner run = new QueryRunner(dataSource);
try
{
run.update("INSERT INTO "+table+ getFields() +
"VALUES " + getParameters(user));
}
catch (SQLException ex)
{
Log.logException(ex);
return false;
}
return true;
}
protected abstract String getFields();
protected abstract String getParameters(T user);
Ugly, and insecure, but okay for transmitting the idea.

It looks like I can give you few simple suggestions for your question.
1)
Instead of managing queries inside DAOs like what you are doing, make a factory class that has list of queries for your needs.
like
class QueryFactory {
static String INSERT_BOOK = "BLAH";
static String DELETE_BOOK = "BLAH";
}
This will separate queries from DAO code and make it easier to manage.
2)
Implement a generic DAO
http://www.codeproject.com/Articles/251166/The-Generic-DAO-pattern-in-Java-with-Spring-3-and
3) As you have mentioned above, use ORM to help yourself binding beans to Database and many more features.

Using DBUtils you have already abstracted away a lot of boilerplate code. What remains is mostly the work that differs between entities : The right sql statement, transformation of entity objects into UPDATE parameters and vice versa with SELECTs, exception handling.
Unfortunately it is not easy to create a general abstraction that is flexible enough for these remaining tasks. That's what ORM mappers are all about. I would still suggest to look into one of these. If you stick to the JPA API, you are still in standards land and able to switch the ORM provider more easily (although there is always some coupling).
I was impressed by SpringData's Repository abstraction. In simple use cases they give you zero code DAO's. If you are already using Spring and just want to persist your object model you should definitely look into it.
Alternatively I made some good experiences with jooq. It can also create DTO's and corresponding DAO's based on the tables in your schema. In contrast to ORM mappers it is closer to the relational schema, which may be an advantage or a disadvantage.

mybatis allows you to return a resulttype as hashmap:
<select id="mCount" resultType="hashmap">
select managerName, count(reportees) AS count
from mgr_employee
group by managerName;
</select>
So you can effectively write out in a workflow like this:
1) Develop an interface
2a) Use mybatis annotation to define the query required OR
2b) Link the interface to a xml and write the query
Take note that this will not be involving any DAO and other boilerplates involve as above

Related

Joined inheritance strategy - good alternative to Hibernate?

I'm currently working on a project where we have a "joined inheritance" database table setup. The setup is essentially similar to the following:
Where the records in each sub-table have a one-to-one relationship with the parent table. Right now, there are only a few sub-tables, but it's possible in the future that we could have many more.
I know that this can be done fairly easily with JPA/Hibernate, but my team is not keen on using it.
Here is the solution I'm leaning on without using Hibernate:
public interface PersonDao<T> {
T create(T t);
// other methods omitted for brevity
}
public class EmployeeDao implements PersonDao<Employee> {
private static final String CREATE_SQL = "INSERT INTO EMPLOYEE...";
#Override
public Employee create(Employee employee) {
// create Employee
}
}
public class OwnerDao implements PersonDao<Owner> {
/* same pattern as EmployeeDao */
}
public class PersonDaoFactory {
public PersonDao dao(String type) {
if (type.equals("OWNER")) {
return new OwnerDao();
} else if (type.equals("EMPLOYEE")) {
return new EmployeeDao();
}
throw new RuntimeException("Could not find DAO for type '" + type + "'");
}
}
My question is, is there a better solution for this scenario in regards to maintainability and good design, without using an ORM like Hibernate? It seems like this will make adding new types a considerable amount of work in comparison to when using Hibernate.

Mapping ResultSet to Pojo Objects

Well that's really embarrassing I have made a standard pojo class and its dao class for data retrieval purpose. I am having a difficulty to understand a basic procedure to how to handle a customized query data to Pojo class.
let's say my User class is
public class User{
private int userId;
private String username;
private int addressId;
}
public class Address{
private int addressId;
private String zip;
}
public class UserDAO{
public void getUserDetails(){
String getSql = select u.userId, u.username, a.zipcode from user u, address a where u.addressId = a.addressId;
//no pojo class is now specific to the resultset returned. so we can't map result to pojo object
}
}
now how I should model this with my pojo class as if using String to manage this then concept of object oriented vanishes, also complexity would increase in the future as well. kindly guide!
Update for Further Explanation
We know that we can map same table objects with same pojo class, but when the query is customized and there is a data returned which doesn't map to any specific class then what would be the procedure? i.e. should we make another class? or should we throw that data in a String variable? kindly give some example as well.
For this purpose you can use one of implementation of JPA. But as you want to do it manually I will give you small example.
UPD:
public class User {
private int userId;
private String username;
private Address address; // USE POJO not ID
}
public class Address{
private int addressId;
private String zip;
List<User> users;
}
public User getUserById(Connection con, long userId) {
PreparedStatement stmt;
String query = "select u.user_id, u.user_name, a.id, a.zip from user u, address a where a.address_id = u.id and u.id = ?";
User user = new User();
Address address = new Address;
try {
stmt = con.prepareStatement(query);
stmt.setLong(1, userId);
ResultSet rs = stmt.executeQuery();
address.setId(rs.getInt("id"));
address.setZip(rs.getString("zip");
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("user_name"));
user.setAddressId(rs.getInt("address_id"));
user.setAddress(address); // look here
} catch (SQLException e) {
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch (SQLException excep) {
}
}
} finally {
if (stmt != null) {
stmt.close();
}
}
return user;
}
You shouldn't do new POJO for that query, you should write normal query. And remember - your object model is main, tables in DB is just a way to save data of your application.
We know that we can map same table objects with same pojo class, but when the query is customized and there is a data returned which doesn't map to any specific class then what would be the procedure? i.e. should we make another class?
JPA dynamic instantiation allows you to define a query with a POJO whose constructor specifies only the fields and types you want from the database.
This will perform a JPA selection which will return a List.
If you need to change the query later and the columns are unchanged, your POJO will still work.
If you change the columns, then also change the POJO accordingly.
NOTE:
You must specify fully qualified package and constructor arguments.
Type User must be a JPA-mapped or JPA-annotated entity class.
The entityManager is in JPA EntityManagerFactory.
TypedQuery<User> q;
String sql = "select new com.stuff.User(
int u.userId, String u.username, String a.zipcode)
from User u, Address a where u.addressId = a.addressId";
List<User> list = entityManager.createQuery(sql).getResultList();
for(User u : list) {
doStuff(u);
}
Dynamic instantiation is also handy when you want to select specified columns, but avoid those columns with large data, such as BLOB types.
For example, maybe you want a list of proxy POJO's which represent the fully populated thing, but are themselves not fully populated.
You present the proxy list, and when the user selects one, then you do another query to get the fully populated object.
Your mileage may vary.
There's many ORM frameworks that can do this including Hibernate, myBatis, JPA and spring-JDBC
spring-jdbc and myBatis give you granular control over the SQL whereas with JPA and Hibernate you are usually abstracted away from the SQL.
I suggest you do some reading and figure out which one you like before rolling your own solution.
Your question:
We know that we can map same table objects with same pojo class,
but when the query is customized and there is a data returned
which doesn't map to any specific class then what would be the procedure?
If you have 100 kinds of SQL which returns different combination of columns, could it be to create 100 different POJOs? The answer is "NO, stop using POJO".
This library qood is designed to solve this problem, you can try it.

How to use Spring jdbc templates (jdbcTemplate or namedParameterJDBCTem) to retrieve values from database

Few days into Spring now. Integrating Spring-JDBC into my web application. I was successfully able to preform CRUD operations on my DB, impressed with boiler-plate code reduction. But I am failing to use the query*() methods provided in NamedParameterJDBCTemplate. Most of the examples on the internet provide the usage of either RowMapper or ResultSetExtractor. Though both uses are fine, it forces me to create classes which have to implement these interfaces. I have to create bean for every type of data I am loading for the DB (or maybe I am mistaken).
Problem arises in code section where I have used something like this:
String query="select username, password from usertable where username=?"
ps=conn.prepareStatement(query);
ps.setString(username);
rs=ps.executeQuery();
if(rs.next()){
String username=rs.getString("username");
String password=rs.getString("password")
//Performs operation on them
}
As these values are not stored in any bean and used directly, I am not able to integrate jdbcTemplate in these kind of situations.
Another situation arises when I am extracting only part of properties present in bean from my database.
Example:
public class MangaBean{
private String author;
private String title;
private String isbn;
private String releaseDate;
private String rating;
//getters and setters
}
Mapper:
public class MangaBeanMapper implements RowMapper<MangaBean>{
#Override
public MangaBean mapRow(ResultSet rs, int arg1) throws SQLException {
MangaBean mb=new MangaBean();
mb.setAuthor(rs.getString("author"));
mb.setTitle(rs.getString("title"));
mb.setIsbn(rs.getString("isbn"));
mb.setReleaseDate(rs.getString("releaseDate"));
mb.setRating(rs.getString("rating"));
return mb;
}
}
The above arrangement runs fine like this:
String query="select * from manga_data where isbn=:isbn"
Map<String, String> paramMap=new HashMap<String, String>();
paramMap.put("isbn", someBean.getIsbn());
return template.query(query, paramMap, new MangaBeanMapper());
However, if I only want to retrieve two/three values from my db, I cannot use the above pattern as it generates a BadSqlGrammarException: releaseDate does not exist in ResultSet . Example :
String query="select title, author where isbn=:isbn"
Map<String, String> paramMap=new HashMap<String, String>();
paramMap.put("isbn", someBean.getIsbn());
return template.query(query, paramMap, new MangaBeanMapper());
Template is an instance of NamedParameterJDBCTemplate. Please advice me solutions for these situations.
The other answers are sensible: you should create a DTO bean, or use the BeanPropertyRowMapper.
But if you want to be able to have more control than the BeanPropertyRowMapper, (or reflection makes it too slow), you can use the
queryForMap
method, which will return you a list of Maps (one per row) with the returned columns as keys. Because you can call get(/* key that is not there */) on a Map without throwing an exception (it will just return null), you can use the same code to populate your object irrespective of which columns you selected.
You don't even need to write your own RowMapper, just use the BeanPropertyRowMapper that spring provides. The way it works is it matches the column names returned to the properties of your bean. Your query has columns that match your bean exactly, if it didn't you would use an as in your select as follows...
-- This query matches a property named matchingName in the bean
select my_column_that doesnt_match as matching_name from mytable;
The BeanPropertyRowMapper should work with both queries you listed.
Typically, yes : for most queries you would create a bean or object to transform the result into. I would suggest that more most cases, that's want you want to do.
However, you can create a RowMapper that maps a result set to a map, instead of a bean, like this. Downside would be be losing the type management of beans, and you'd be relying on your jdbc driver to return the correct type for each column.
As #NimChimpskey has just posted, it's best to create a tiny bean object : but if you really don't want to do that, this is another option.
class SimpleRowMapper implements RowMapper<Map<String, Object>> {
String[] columns;
SimpleRowMapper(String[] columns) {
this.columns = columns;
}
#Override
public Map<String, Object> mapRow(ResultSet resultSet, int i) throws SQLException {
Map<String, Object> rowAsMap = new HashMap<String, Object>();
for (String column : columns) {
rowAsMap.put(column, resultSet.getObject(column));
}
return rowAsMap;
}
}
In yr first example I would just create a DTO Bean/Value object to store them. There is a reason its a commonly implemented pattern, it takes minutes to code and provides many long term benefits.
In your second example, create a second implementation of rowmapper where you don;t set the fields, or supply a null/subsitute value to mangabean where necessary :
#Override
public MangaBean mapRow(ResultSet rs, int arg1) throws SQLException {
MangaBean mb=new MangaBean();
mb.setAuthor(rs.getString("author"));
mb.setTitle(rs.getString("title"));
/* mb.setIsbn("unknown");*/
mb.setReleaseDate("unknown");
mb.setRating(null);
return mb;
}

Java/Spring: Beans for the View

I have a dilemma and I'm not sure about the best way to start solving it.
I'm working with an old code base at work. Some of the domain objects (and db tables behind them) don't make a lot of sense. eg, deleted is stored as a long, age is stored as a String, etc. Which I've been able to work with fine. In the view I can say if (deleted == 1).... But there are some specific business logic that is leading to a maintenance problem by having it in the view. Here's one example:
String title = null;
if (obj.getTitle != null) {
title = obj.getTitle();
} else {
title = obj.getName() + " - " + obj.getCategory();
}
I would really like to have a "view bean" where this business logic and legit oddities are ironed out and stored so that I can use it in multiple views but then change it in one place. If I had a Product POJO and then my ProductViewBean, as an example, I would do something like:
productViewBean.setDeleted( product.getDeleted() == 1 );
productViewBean.setTitle( product.getTitle() != null ? product.getTitle() : product.getName() + " - " + product.getCategory() );
My question is, where should I do this? Should I have a manager (with appropriate daos injected into it) that is injected into my controller and returns my "view bean"? Or am I going about this all wrong and could there be better approach?
Thanks in advance
(Note: I understand that the underlying structure is the real problem but it is beyond my jurisdiction to change at this point. Too many projects use these domain objects. And even if I did clean up the db/domain objects (so that deleted was a boolean etc) I'm still left with unavoidable business logic (if !title then 'build title from other components') that doesn't belong in the data layer and that I would like to encapsulate in one place so that neither the controller or view have to worry about it and it can be used across multiple controllers/views. I happen to be at a point where I can write something that is efficient and maintainable and could even create a good layer to ease cleanup of these domain objects in the future.)
I'll bet you can sort all that out in the Spring data binding and validation API.
I'd also say that you should have a service tier that's distinct from the web controller layer. Inject the services into the web tier and let them do all the work. They worry about units of work, transactions, and DAO objects.
I would try an Adapter delegating to the domain object like the following. The controller and view use this one. If the ProductViewBean is in the same package as the manager, the manager only can use the getDelegate() method to pass it to the dao.
public class ProductViewBean {
private final Product delegate;
public ProductViewBean(Product delegate) {
this.delegate = delegate;
}
Product getDelegate() {
return delegate;
}
public String getTitle() {
if (delegate.getTitle == null) {
return delegate.getName() + " - " + delegate.getCategory();
}
return delegate.getTitle();
}
public void setTitle(String title) {
delegate.setTitle(title);
}
public boolean isDeleted() {
return delegate.getDeleted() == 1L;
}
public void setDeleted(boolean deleted) {
delegate.setDeleted(deleted ? 1L : 0L);
}
...
}
So you can make the API you like.

JPA: How do I specify the table name corresponding to a class at runtime?

(note: I'm quite familiar with Java, but not with Hibernate or JPA - yet :) )
I want to write an application which talks to a DB2/400 database through JPA and I have now that I can get all entries in the table and list them to System.out (used MyEclipse to reverse engineer). I understand that the #Table annotation results in the name being statically compiled with the class, but I need to be able to work with a table where the name and schema are provided at runtime (their defintion are the same, but we have many of them).
Apparently this is not SO easy to do, and I'd appreciate a hint.
I have currently chosen Hibernate as the JPA provider, as it can handle that these database tables are not journalled.
So, the question is, how can I at runtime tell the Hibernate implementation of JPA that class A corresponds to database table B?
(edit: an overridden tableName() in the Hibernate NamingStrategy may allow me to work around this intrinsic limitation, but I still would prefer a vendor agnostic JPA solution)
You need to use the XML version of the configuration rather than the annotations. That way you can dynamically generate the XML at runtime.
Or maybe something like Dynamic JPA would interest you?
I think it's necessary to further clarify the issues with this problem.
The first question is: are the set of tables where an entity can be stored known? By this I mean you aren't dynamically creating tables at runtime and wanting to associate entities with them. This scenario calls for, say, three tables to be known at compile-time. If that is the case you can possibly use JPA inheritance. The OpenJPA documentation details the table per class inheritance strategy.
The advantage of this method is that it is pure JPA. It comes with limitations however, being that the tables have to be known and you can't easily change which table a given object is stored in (if that's a requirement for you), just like objects in OO systems don't generally change class or type.
If you want this to be truly dynamic and to move entities between tables (essentially) then I'm not sure JPA is the right tool for you. An awful lot of magic goes into making JPA work including load-time weaving (instrumentation) and usually one or more levels of caching. What's more the entity manager needs to record changes and handle updates of managed objects. There is no easy facility that I know of to instruct the entity manager that a given entity should be stored in one table or another.
Such a move operation would implicitly require a delete from one table and insertion into another. If there are child entities this gets more difficult. Not impossible mind you but it's such an unusual corner case I'm not sure anyone would ever bother.
A lower-level SQL/JDBC framework such as Ibatis may be a better bet as it will give you the control that you want.
I've also given thought to dynamically changing or assigning at annotations at runtime. While I'm not yet sure if that's even possible, even if it is I'm not sure it'd necessarily help. I can't imagine an entity manager or the caching not getting hopelessly confused by that kind of thing happening.
The other possibility I thought of was dynamically creating subclasses at runtime (as anonymous subclasses) but that still has the annotation problem and again I'm not sure how you add that to an existing persistence unit.
It might help if you provided some more detail on what you're doing and why. Whatever it is though, I'm leaning towards thinking you need to rethink what you're doing or how you're doing it or you need to pick a different persistence technology.
You may be able to specify the table name at load time via a custom ClassLoader that re-writes the #Table annotation on classes as they are loaded. At the moment, I am not 100% sure how you would ensure Hibernate is loading its classes via this ClassLoader.
Classes are re-written using the ASM bytecode framework.
Warning: These classes are experimental.
public class TableClassLoader extends ClassLoader {
private final Map<String, String> tablesByClassName;
public TableClassLoader(Map<String, String> tablesByClassName) {
super();
this.tablesByClassName = tablesByClassName;
}
public TableClassLoader(Map<String, String> tablesByClassName, ClassLoader parent) {
super(parent);
this.tablesByClassName = tablesByClassName;
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (tablesByClassName.containsKey(name)) {
String table = tablesByClassName.get(name);
return loadCustomizedClass(name, table);
} else {
return super.loadClass(name);
}
}
public Class<?> loadCustomizedClass(String className, String table) throws ClassNotFoundException {
try {
String resourceName = getResourceName(className);
InputStream inputStream = super.getResourceAsStream(resourceName);
ClassReader classReader = new ClassReader(inputStream);
ClassWriter classWriter = new ClassWriter(0);
classReader.accept(new TableClassVisitor(classWriter, table), 0);
byte[] classByteArray = classWriter.toByteArray();
return super.defineClass(className, classByteArray, 0, classByteArray.length);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private String getResourceName(String className) {
Type type = Type.getObjectType(className);
String internalName = type.getInternalName();
return internalName.replaceAll("\\.", "/") + ".class";
}
}
The TableClassLoader relies on the TableClassVisitor to catch the visitAnnotation method calls:
public class TableClassVisitor extends ClassAdapter {
private static final String tableDesc = Type.getDescriptor(Table.class);
private final String table;
public TableClassVisitor(ClassVisitor visitor, String table) {
super(visitor);
this.table = table;
}
#Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
AnnotationVisitor annotationVisitor;
if (desc.equals(tableDesc)) {
annotationVisitor = new TableAnnotationVisitor(super.visitAnnotation(desc, visible), table);
} else {
annotationVisitor = super.visitAnnotation(desc, visible);
}
return annotationVisitor;
}
}
The TableAnnotationVisitor is ultimately responsible for changing the name field of the #Table annotation:
public class TableAnnotationVisitor extends AnnotationAdapter {
public final String table;
public TableAnnotationVisitor(AnnotationVisitor visitor, String table) {
super(visitor);
this.table = table;
}
#Override
public void visit(String name, Object value) {
if (name.equals("name")) {
super.visit(name, table);
} else {
super.visit(name, value);
}
}
}
Because I didn't happen to find an AnnotationAdapter class in ASM's library, here is one I made myself:
public class AnnotationAdapter implements AnnotationVisitor {
private final AnnotationVisitor visitor;
public AnnotationAdapter(AnnotationVisitor visitor) {
this.visitor = visitor;
}
#Override
public void visit(String name, Object value) {
visitor.visit(name, value);
}
#Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
return visitor.visitAnnotation(name, desc);
}
#Override
public AnnotationVisitor visitArray(String name) {
return visitor.visitArray(name);
}
#Override
public void visitEnd() {
visitor.visitEnd();
}
#Override
public void visitEnum(String name, String desc, String value) {
visitor.visitEnum(name, desc, value);
}
}
It sounds to me like what you're after is Overriding the JPA Annotations with an ORM.xml.
This will allow you to specify the Annotations but then override them only where they change. I've done the same to override the schema in the #Table annotation as it changes between my environments.
Using this approach you can also override the table name on individual entities.
[Updating this answer as it's not well documented and someone else may find it useful]
Here's my orm.xml file (note that I am only overriding the schema and leaving the other JPA & Hibernate annotations alone, however changing the table here is totally possible. Also note that I am annotating on the field not the Getter)
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings
xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd"
version="1.0">
<package>models.jpa.eglobal</package>
<entity class="MyEntityOne" access="FIELD">
<table name="ENTITY_ONE" schema="MY_SCHEMA"/>
</entity>
<entity class="MyEntityTwo" access="FIELD">
<table name="ENTITY_TWO" schema="MY_SCHEMA"/>
</entity>
</entity-mappings>
as alternative of XML configuration, you may want to dynamically generate java class with annotation using your preferred bytecode manipulation framework
If you don't mind binding your self to Hibernate, you could use some of the methods described at https://www.hibernate.org/171.html . You may find your self using quite a few hibernate annotations depending on the complexity of your data, as they go above and beyond the JPA spec, so it may be a small price to pay.

Categories