Sunday, December 17, 2006

Smart Key Generator ejb3


Java Persistence provides facility for auto-generation of primary keys. But limited to integer datatype. Further there is no convenient way to access these generated values. Its often needed that the primary key is a combination of identifiable data. Such keys are also known as Smart Keys.
Smart Keys also need automatic serial number generation.
Following solution uses HiLo sequence generation algorithm.

The solution contains one Stateless Session Bean and a Singleton. Additionally it consists an Entity (JPA)


package jkook.vetan.utils;
/*
* HrSequenceFacade.java
*/
@Stateless
public class HrSequenceFacade implements HrSequenceFacadeLocal, HrSequenceFacadeRemote {


@PersistenceContext
private EntityManager em;

private UIDDispenser uiddispenser;
private static final Logger log = Logger.getLogger(HrSequence.class.getName());

/** Creates a new instance of HrSequenceFacade */
public HrSequenceFacade() {

}

public void create(HrSequence hrSequence) {
em.persist(hrSequence);
}


public void edit(HrSequence hrSequence) {
em.merge(hrSequence);
em.flush();
log.info("HrSequence Updated");

}

public void destroy(HrSequence hrSequence) {
em.merge(hrSequence);
em.remove(hrSequence);
}

public HrSequence find(Object pk) {
return (HrSequence) em.find(HrSequence.class, pk);
}

public List findAll() {
return em.createQuery("select object(o) from HrSequence as o").getResultList();
}

/*
if dispenser is null
create new dispenser
getnextHi from dispenser

*/

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public Integer getNextID(String entityname) {
log.info(" getNextId called");
uiddispenser = UIDDispenser.getDispenser(em);
return uiddispenser.getNextID(entityname,em);
}

public Integer getCount() {
Query q = em.createQuery("select count(o) from HrSequence as o ");
Long count = (Long)q.getSingleResult();
if(count != null){
return count.intValue();
}
return null;
}

public Integer getCurrentID(String entityname) {
uiddispenser = UIDDispenser.getDispenser(em);
return uiddispenser.getCurrentID(entityname);
}
}



/*
* UIDDispenser.java
*
*/

package jkook.vetan.utils;

public class UIDDispenser {


private static UIDDispenser uiddispenser;
private Map allentries;
private static final byte maxLo = 25;
static Logger log = Logger.getLogger(UIDDispenser.class.getName());
/** Creates a new instance of UIDDispenser */
private UIDDispenser() {

}

private UIDDispenser(EntityManager em){
// populate all entries
Iterator i =
em.createQuery("select object(o) from HrSequence as o")
.getResultList()
.iterator();
// for large database initiate the hashmap with count of HrSequence
allentries = new HashMap();
while(i.hasNext()){
HrSequence hs = i.next();
hs.setNextHi(hs.getNextHi()+maxLo);
allentries.put(hs.getEntityname(),hs);
System.err.println(" enityties +++ "+hs.getEntityname());
}

}

public static synchronized UIDDispenser getDispenser( EntityManager em) {
if (uiddispenser == null)
return new UIDDispenser(em);
return uiddispenser;

}

/*
* get nextLo
if lo > max_lo getnext HI
set lo=0
return HI+Lo
*
*
*/
public synchronized Integer getNextID(String entityname, EntityManager em){

HrSequence hs = allentries.get(entityname);
if(hs.getLo() >=maxLo){
log.fine(" LO Val is > maxLo " +hs.getLo() + " maxLo is " + maxLo );
hs.setNextHi(hs.getNextHi()+maxLo);
em.merge(hs);
byte b=0;
hs.setLo(b);
log.fine("Updated HI_VAL");
}
return hs.getNextHi()+ hs.getNextLo();
}

public Integer getCurrentID(String entityname){
HrSequence hs = allentries.get(entityname);
return hs.getNextHi()+ hs.getLo();
}
}

Usage :
@EJB HrSequenceFacade hrSequenceFacade
{
....
....
// start of smart key generation
// smartkey part1
// smartkey part2
Integer nextSerial = hrSequenceFacade.getNextID("NameityOfTheEnt")
// primaryKey = smartkeypart1 + part2 + nextSerial



Ref : Using Primary Keys with Java Persistence

Friday, December 01, 2006

Choosing Primary Keys

Exert from OracleFAQ

  1. The candidate key must be unique within its domain (the entity it represents, and beyond, if you also intend to
    access external entities).
  2. The candidate key can not hold NULL values (NULL is not zero. Zero is a number. NULL is 'nonexistent value').
  3. The candidate key can never change. It must hold the same value for a given occurrence of an entity for the lifetime of that entity.

BizSutra adds

PrimaryKey has to be HUMAN Readable

There are many strategies for identifying a Primary Key. With advancement of OOP the debate for surrogate keys has elongated. The fact remains that the strength of Object Oriented Programming is readability of the code. This capability has to be extended to persistence layer. Using surrogate keys defeats the above capability. Having Mnemonic code in foreign key fields is a good practice. Gurus suggest is 8.

Data Live Longer Than the Logic Accessing the Data

BizSutras POC Application

BizSutras promote good practices. These are not best practices. One will find there are more than one alternative sutra for each of them.

As a proof of concept we plan to develop a simple but usable application. Following are the creteria for selecting a Business Use-Case

  1. Shoud not be an acadamic application like petstore
  2. Has to be WEB-BASED
  3. Has to be usable out-of the box
  4. Quick to develop
  5. Which can demonstrate most of the Business Application Development Scenarios

The final choice is Vetan (Paroll)

There are very few paroll and HR applications in opensource. But seems there is very good demand. This is relatively well know domain. Market is still fragmented.

Initial code base is going to be derived from OrangeHRM. We are going to reuse the data model.