A lot of folk have asked me how I do the marshalling of data between code. As I don’t have a heavy reliance on frameworks, see yesterday’s post for more on that topic, I roll my own code out to do the job. The main reason for this is control I like to see where things break, if I can tweak performance (good SQL and indexing DO help) and generally have a more positive feel that my code is working.
First things first, we need an object to put some data into…. My objects mirror database tables and each have a manager and another class to handle any database work. The connection pool resides within the container that runs the application (it’s usually Tomcat or Orion if it’s a web app). Right a plain old Java object holds our values. Call it a bean, an object, a model or a POJO – not really concerned….
package com.datasentiment.core.types;public class APNSMessage {private int id = 0;private String sound = "default";private String message = "";private String devicename = "";private String devicetoken = "";// getters and setters to handle the values.... your IDE will generate them}
Next part we need is a class that will sit between our database logic. Reason is this, if I decide that change the database at any stage then the major refactoring comes from the data access object and not all the view layer. Cuts down on time, it’s a handy trade off. The AbstractFactory design pattern sits nicely here and uses the class loader to haul in the DAO object (we’ll create that in a minute) into the JVM. One shot use of classes got to love it all. The abstract functions at the bottom are the ones you’ll use to create, delete the POJO’s that you send to the database.
package com.datasentiment.core.managers;import com.datasentiment.core.exceptions.WritingException;import com.datasentiment.core.types.APNSMessage;public abstract class APNSMessageMngr { private static APNSMessageMngr instance = null; private static synchronized void createInstance() { if (instance == null) { Class mgr = null; APNSMessageMngr um = null; try { String classname = "com.datasentiment.core.dao.APNSMessageDAO"; mgr = Thread.currentThread().getContextClassLoader().loadClass( classname); } catch (ClassNotFoundException e) { e.printStackTrace(); } try { um = (APNSMessageMngr) mgr.newInstance(); instance = um; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } public static APNSMessageMngr getInstance() { if (instance == null) { createInstance(); } return instance; } protected APNSMessageMngr() { } public abstract void create(APNSMessage m) throws WritingException; public abstract void delete(APNSMessage m) throws WritingException;}
Last thing we need is the data access object (DAO) to do the work with the database itself. While I use raw SQL queries here, I’m a pedant like that, you could have any ORM framework (like Hibernate for example) to do the dirty work for you. Performance in the SQL is key for me so I prefer to have the control over these things. I’ve shown the class below with one of the methods defined in the Abstract Factory pattern (public void delete()).
package com.datasentiment.core.dao;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;import javax.naming.Context;import javax.naming.InitialContext;import javax.sql.DataSource;import com.datasentiment.core.exceptions.WritingException;import com.datasentiment.core.managers.APNSMessageMngr;import com.datasentiment.core.types.APNSMessage;public class APNSMessageDAO extends APNSMessageMngr{ @Overridepublic void delete(APNSMessage m) throws WritingException { PreparedStatement pstmt = null; Context ctx = null; DataSource ds = null; Connection c = null; try { ctx = new InitialContext(); ds = (DataSource) ctx.lookup("java:comp/env/jdbc/datasentiment"); c = ds.getConnection(); pstmt = c.prepareStatement("UPDATE apnsmessage SET deleteflag=UNIX_TIMESTAMP() WHERE id=?"); pstmt.clearParameters(); pstmt.setInt(1, m.getId()); pstmt.execute(); } catch (Exception e) { e.printStackTrace(); } finally { try { pstmt.close(); c.close(); } catch (SQLException e) { e.printStackTrace(); } }} }
That’s it, calling our system from within code is easy enough.
APNSMessageMngr amm = APNSMessageMngr.getInstance(); APNSMessage message = new APNSMessage(); message.setMessage("Don't push me cos I'm close to the edge"); message.setDevicename("mydevice"); amm.create(message);
2 responses to “[Trenches Tip] – Rolling your own AbstractFactory for data access.”
Hi John,In exactly the same way. Those methods in the abstract factory class were basic voids. I’d usually have a lot more going on in there. I also make a habit of generating my own ids on object creation as well has having a primary key auto_increment within the database. Keeps things a lot cleaner.CheersJ
I would handle the caching in a higher layer, the layer that’s going to process the list of information.So if the list doesn’t exist in the cache then go and call the DAO (via the manager) to get it.