Java tutorial
/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.store.hibernate.id; import java.io.Serializable; import java.lang.reflect.Constructor; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.dialect.Dialect; import org.hibernate.engine.SessionImplementor; import org.hibernate.engine.TransactionHelper; import org.hibernate.id.Configurable; import org.hibernate.id.IdentifierGenerationException; import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.type.Type; import org.hibernate.util.PropertiesHelper; import org.hibernate.util.ReflectHelper; /** * Class for generating keys for our "user typed" identifiers. Specify table and idClassName, where idClassName is the actual domain class * to instantiate passing an Integer to the constructor. */ public class IntegerBasedCustomIdentifierGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable { private static final String SELECT_LASTKEY = "SELECT key_llastkey FROM tkey WHERE key_sTableName = ?"; private static final String UPDATE_LASTKEY = "UPDATE tKey SET key_lLastKey = ( key_lLastKey + ? ) WHERE key_sTableName = ?"; private static final String INSERT_NEWKEY = "INSERT INTO tKey ( key_sTableName, key_lLastKey ) VALUES ( ?, ? )"; private static final String TABLE = "table"; private static final String ID_CLASS_NAME = "idClassName"; private String tableName; private Class idClass; public String[] sqlCreateStrings(Dialect dialect) throws HibernateException { StringBuffer sql = new StringBuffer(); sql.append("CREATE TABLE tKey ("); sql.append(" key_sTableName varchar(18) not null"); sql.append(",key_lLastKey integer not null"); sql.append(",primary key (key_sTableName)"); sql.append(" )"); return new String[] { sql.toString() }; } public String[] sqlDropStrings(Dialect dialect) throws HibernateException { StringBuffer sql = new StringBuffer(); sql.append("DROP TABLE tKey"); return new String[] { sql.toString() }; } public Object generatorKey() { return this.getClass().getName(); } public void configure(Type type, Properties params, Dialect d) throws MappingException { idClass = parseClass(PropertiesHelper.getString(ID_CLASS_NAME, params, null)); tableName = PropertiesHelper.getString(TABLE, params, null); if (tableName == null) { throw new IllegalArgumentException("Property '" + TABLE + "' is not set"); } tableName = tableName.toLowerCase(); } private Class parseClass(String className) { try { return ReflectHelper.classForName(className); } catch (ClassNotFoundException e) { throw new MappingException("Failed to parse class: " + className, e); } } public Serializable generate(SessionImplementor session, Object object) throws HibernateException { return doWorkInNewTransaction(session); } protected Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException { try { updateNextKey(conn, tableName, 1); } catch (KeyRowNotFoundException e) { // insert row for tableName and try one more time insertNewKey(conn, tableName); updateNextKey(conn, tableName, 1); } Integer nextKey = selectLastKey(conn, tableName); return convertToUserType(nextKey); } private Serializable convertToUserType(Integer value) { try { Constructor constructor = idClass.getConstructor(new Class[] { Integer.class }); return (Serializable) constructor.newInstance(value); } catch (Exception e) { throw new RuntimeException("Failed to instantiate (" + value + "). " + idClass + " probably do not have a constructor that takes only one Integer.", e); } } private void updateNextKey(Connection conn, String tableName, int steps) throws SQLException, KeyRowNotFoundException { PreparedStatement ps = conn.prepareStatement(UPDATE_LASTKEY); try { ps.setInt(1, steps); ps.setString(2, tableName); int result = ps.executeUpdate(); if (result < 1) { throw new KeyRowNotFoundException(); } } finally { closeStatement(ps); } } private void insertNewKey(Connection conn, String tableName) throws SQLException { PreparedStatement ps = conn.prepareStatement(INSERT_NEWKEY); try { ps.setString(1, tableName); ps.setInt(2, 0); ps.executeUpdate(); } finally { closeStatement(ps); } } private Integer selectLastKey(Connection conn, String tableName) throws SQLException { PreparedStatement ps = conn.prepareStatement(SELECT_LASTKEY); try { ps.setString(1, tableName); ResultSet rs = ps.executeQuery(); if (!rs.next()) { String msg = "Failed to read next key value for table '" + tableName + "', no row."; throw new IdentifierGenerationException(msg); } Integer lastKey = rs.getInt(1); if (rs.wasNull()) { String msg = "Failed to read next key value for table '" + tableName + "', key_llastkey was null."; throw new IdentifierGenerationException(msg); } closeResultSet(rs); return lastKey; } finally { closeStatement(ps); } } private void closeResultSet(ResultSet rs) throws SQLException { if (rs != null) { rs.close(); } } private void closeStatement(PreparedStatement ps) throws SQLException { if (ps != null) { ps.close(); } } private class KeyRowNotFoundException extends RuntimeException { } }