Hibernate Transactions - Part 1 - How to Wrap...

Just had a call from someone wanting to know how to use Hibernate to delete some data from a table. Hibernate is extremely powerful when transactions are used correctly. First of all, lets start with where to "wrap" the transactions...

Most J2EE architecture is built like this (or should be!):

  1. Actions (Struts)
  2. Services
  3. DAOs (Data access objects)
One minor note is that Actions should NEVER have DAOs in them.  No reason.  Never.  The question you should have in the coding of any action is this.  "If I want to do this in the action, will I need to recode if I want to add a REST service to do this?"  Let me explain.  If you put ALL business logic and database calls in the Service layer, it is nothing to put a REST service to access it.  If you put DAO calls (or any business logic) in the Actions, you will have to cut and paste that code into a service to put a REST service on the same logic.  The following is brilliant service code!

public List getAllActiveLegacyFeatures() throws Exception {
  List features = fpsDao.grabAllActiveLegacyFeatures();
  return features;
 }

Moving on.  So you really only have 2 choices.  Wrap the transactions at the DAO layer or the Service layer. Lets begin by showing you how to "Wrap" a transaction.  Here is some code that does it:

<bean id="ImportKeyDataServiceImplementation" class="cat.dds.fpsdma.services.ImportKeyDataService">
  <constructor-arg index="0" ref="fpsDAO" />
 </bean>
 <bean id="ImportKeyDataService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref bean="theTransactionManager" />
  </property>
  <property name="target">
   <ref local="ImportKeyDataServiceImplementation" />
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="*">PROPAGATION_REQUIRED</prop>
   </props>
  </property>
 </bean>

The first "bean" here is the actual object. Notice that on line 2 it has a DAO. Now this object should never be used, so we actually call it an "...Implementation". Notice that the second bean is named "ImportKeyDataService" It is not an implementation, but the object that we should use. (This is just how we did it on FPS. We just used this naming convention so we new that we should never use the "Implementation" objects). Notice that the Implementation object is referenced on line 9. This ties all this together. So, when you actually use the object "ImportKeyDataService" you will get a Wrapped transaction.

What does this give you? Lots. When any DAO call is made in the ImportKeyDataService object above, Hibernate will not commit anything. Batch updates and such automatically roll back correctly if there is an error thrown out of the ImportKeyDataService object. This would not happen if you wrapped the DAO. Every time you go in and back out of an object (the object looses scope) you commit. So, if you have a DAO doing something, it would commit every time you made a call.

Now there are two times when you want the DAO to be wrapped.

  1. If you are doing DB2 calls to CDID or some alien database, and you are just doing "selects", it is best to commit after every transaction.  For some reason Mainframers like that.
  2. JUnits usually require commits to work properly.  We make a junit for EVERY database call.  They just don't work if you don't commit after each one.  However, in this case, Spring makes this easy.
The most powerful thing about Wrapping transactions, though, is the "Lazy" fetching.  I will talk about that in the next post...

Find a Duplicate File...

We had a problem with duplicate XML files showing up to be loaded in an app.  After a quick search on the web, the suggestion was to do Checksums. I came up with this:

private boolean isDuplicateFile(String fileName, String path, String backupPath) throws IOException, FileNotFoundException {

  File theFile = new File(MEDUtils.addToFilePath(path, fileName));
  if (!theFile.exists()) {
   throw new FileNotFoundException(MEDUtils.addToFilePath(path, fileName));
  }
  long fileChecksum = FileUtils.checksumCRC32(theFile);

  File[] files = new File(backupPath).listFiles();
  long[] checksums = new long[files.length];
  for (int i = 0; i < files.length; i++) {
   long checksum = FileUtils.checksumCRC32(files[i]);
   new MEDInfoEvent(this, "checkForDuplicateFiles()", "File name: " + files[i].getName() + " checksum: [" + checksum + "]");
   checksums[i] = checksum;
  }
  return ArrayUtils.contains(checksums, fileChecksum);
 }

Cool SQL...

Here are some cool SQL tricks that I have learned...

Recently I had to order some some data but be case insensitive. I did it in Java using a Comparable, but this sql can do it also:
select *
from field_units
ORDER BY Lower(unit_metric_name)
This can find duplicate rows in a table:
select cust_id, site, count(*)
from alliance_customer_sites
group by cust_id, site
having count(*) > 1
This will, in Oracle, find all the tables in a schema:
select table_name, num_rows counter
from dba_tables
where owner = 'SCHEMA_NAME'
order by table_name;
This does the same in DB2 land:
select (SUBSTR(TBNAME, 1,30)) as Table, COLNO, (SUBSTR(NAME, 1,30)) as Column, COLTYPE, LENGTH, SCALE, DEFAULT, NULLS
from SysIBM.SysColumns
WHERE TBCreator = 'Z1Z10001$'
Here is a basic loop in Oracle:
BEGIN
--must have projects table loaded first...
--obviously you can join tables from different databases. Cool!
DELETE FROM project_commodities;
DECLARE
CURSOR c1
IS
select pc.proj_id, pc.type, ct.commodity_id
from project_commodities@z1sh pc, Commodity_types ct
where pc.TYPE = ct.COMMODITY_TYPE
order by proj_id ;
BEGIN
FOR x IN c1
LOOP
INSERT INTO project_commodities VALUES (x.proj_id, x.type, x.commodity_id) ;
END LOOP;
COMMIT;
END;
END;
This will find a stored procedure in Oracle:
this will find a stored proc.
select dbms_metadata.get_ddl('PROCEDURE','Stored_proc_name') FROM DUAL;
In DB2 land users need to add "WITH UR" to the end of all Select statements. In Hibernate, this is quite difficult, until you understand the power of the HibernateInterceptor.
  1. Extend  org.hibernate.EmptyInterceptor.
  2. Override the onPrepareStatement() method
  3. This method has a String argument which is what Hibernate thinks the final SQL should look like.  Just append " with ur" to the end of it and go.
  4. Remember, ALL sql goes through this, so what I have done in the past is add some "if" logic and look for the word Insert, update, delete etc (StringUtils.containsIgnoreCase(arg0, "insert")).  Don't add "with ur" to this.
 Another tip in DB2 land.  Strings and Chars are a pain as are Doubles and BigDecimal stuff.  Extend org.hibernate.dialect.DB2390Dialect and in the constructor you can "register" types:
public DB2DialectExtension() {
        super();
        registerHibernateType(Types.CHAR, Hibernate.STRING.getName());
        registerHibernateType(Types.DECIMAL, Hibernate.DOUBLE.getName());
    }
Doing this will keep the model beans nice and simple.  And nobody uses Char's anymore anyway...
These last two are hooked up this way:


 
 
 
  
 
 
  
   cat.dds.med.utils.DB2DialectExtension
   false
   none
   org.hibernate.cache.EhCacheProvider
   true
   true
   jndi/hibernate/medSessionFactory
   true
  
 

  1. Line 1 is defining the Interceptor.
  2. Line 5-7 are hooking it up.
  3. Line 10 is using the new Dialect.
Sweet!

Visualizing HypersonicDB data in JUnit

We all know that HypersonicDB has a GUI client. But, I could never figure out how to use that client in a JUnit running Hypersonic as a memory-only DB. Until now...


public void startHSQLGUI() {
String[] strings = new String[]{"-driver", "org.hsqldb.jdbcDriver", "-url", "jdbc:hsqldb:mem:appTempl", "-user", "sa", "-password", ""};
DatabaseManagerSwing.main(strings);
}


You need to change the -user and the -password options as appropriate.

Now, put a breakpoint somewhere in your code and fire that method when the breakpoint gets hit (in RAD, I open the "Display" view and execute startHSQLGUI()).

One problem tho...as with most fat client GUIs, closing the GUI apparently issues a System.exit() and will kill the JVM...

Execute Non-Public Methods...

Tim always trys to get me to test Actions and I don't want to. That is what Selenium is for... Anyway, there are some methods in Actions that need testing, like validations and such. Here is a method that Tim wrote a while back that I find indespensible:
public Object invokeNonPublicMethod(Object o, String methodName, Object[] args) 
                                                 throws Exception {
  assertNotNull(o);
  assertNotNull(methodName);
  Method methods[] = o.getClass().getDeclaredMethods();
  for (int i = 0; i < methods.length; ++i) {
   if (methodName.equals(methods[i].getName())) {
    try {
     methods[i].setAccessible(true);
     return methods[i].invoke(o, args);
    } catch (Exception e) {
     throw new Exception(e.getCause());
    }
   }
  }
  fail("Method '" + methodName + "' not found in " + o.getClass());
  return null;
 }  
You execute it like this:
FPSActionMessages messages = (FPSActionMessages) invokeNonPublicMethod(action, "validateSearchCriteria", new Object[]{form});
Sweet and simple.  We use it everywhere...

How to Rotate Files...

Just finished a project were I had to delete files older than 5 days.  This uses commons-io1.4.jar.

private void shuffleFiles() {
        try {
            FileFilter fileFilter = new FileFilter() {
                public boolean accept(File pathname) {
                    return FileUtils.isFileOlder(pathname, DateUtils.addDays(new Date(), -5));
                }
            };
            File[] files = new File(MEDUtils.getBackupFileDirectory()).listFiles(fileFilter);
            for (int i = 0; i < files.length; i++) {
                new MEDInfoEvent(this, "shuffleFiles()", "File deleted: [" + files[i].getName() + "]");
                FileUtils.deleteQuietly(files[i]);
            }
        } catch (Exception e) {
            new MEDFatalEvent(this, "shuffleFiles()", "File shuffling failed!", e);
        }
    }