Monday, May 19, 2008

Let's Go Lazy

It needs a minuite to load Financials.htm, and needs more minuites to load EditFinancial.htm, sometimes permGem error, and the dumb thing is I thought it just perhaps the database server is backuping or doing something which make it slow. And the dumberer thing is I try to use other database server, I create the tables I need one by one, and then I load those data. And the dumbest thing is I feel my application is going faster with the new database although I can write email, download something, or perhaps watching Thomas Cup while waiting my form's load. It's more than Dumb and Dumberer the movies. It was Dumb, Dumberer, and Dumbest.

The Financial table only need CURRENCY_KEY, LOCATION_KEY, etc.. it doesn't need to have the DimCurrency, DimLocation, etc whole as an object to have my Financial object, so I donot need to write extra lazy="false" in my Pojo's XDoclet. It was my bad habit to copy=paste everything without comparing my needs, I copy the Xdoclet declaration from other source that turn not to be lazy one. Hibernate is automatically set to be lazy by default. here's taken from my DimCurrency:

/**
* @return Returns the financials.
*
* @hibernate.bag name="financials" lazy="true" cascade="all"
* @hibernate.collection-key column="CURRENCY_KEY"
* @hibernate.collection-one-to-many class="id.co.model.Financial"
*/
public List getFinancials() {
return financials;
}
public void setFinancials(List financials) {
this.financials = financials;
}

It's still gimme error after I set lazy, the something named GGG error or something. I browse the net and it says that it needs an empty constructor for the pojos, wheter the Financial or the Dims, I forgot it, I just add it into all my pojos. sorry I missed the link, I write this post after repair my application, set it to lazy, and everything's go faster after the lazy things. just let's go to be l[cr]azy for a while!

Friday, May 16, 2008

Updating Composite-id Object

Before continuing, I use appfuse-spring-hibernate, FYI. Ok, I already had Financial object with composite keys that I generate directly from my Pojo, now I get error if I update a record of it.

It's just said that it failed to convert from String to FinancialPk. And when I debug it, in FinancialFormController, it never enter the onSubmit method, there's same error when in processFormSubmission method.

This error is happened for two days, I already browse the net, asked some people, but still don't get the answer, so I encourage myself to ask to JUG, thank's I've got little and expensive clue:
PropertyEditor, so here's the steps, because it make me easier when I wrote it in walktrough style :
  • I need bind the id type in initBinder method in FInancialFormController, that error says that system cannot convert String to FinancialPk type. So I add it together with Date type and numbers type.
  • Before that I need to override the getAsText and setAsText, methods from PropertyEditor.
  • First I made FinancialPk implements PropertyEditor and add those methods, and I wrote in initBinder: binder.registerCustomEditor(FinancialPk.class, new FinancialPk()); but it results an error.
  • So, I browse the net and I need new class called FinancialPkSupport that extends PropertyEditorSupport which is an adapter of PropertyEditor interface, make a constructor that have one parameter: Class pkClass. And ofcourse, override those to methods.
  • And then in inintBinder in FinancialFormController I add this line: binder.registerCustomEditor(FinancialPk.class, new FinancialPkSupport(FinancialPk.class));
Here's my FinancialPkSupport:

public class FinancialPkSupport extends PropertyEditorSupport {
private Class pkClass;

public FinancialPkSupport() {}
public FinancialPkSupport(Class pkClass) {
this.pkClass = pkClass;
}

public String getAsText() {
Object value = getValue();
return value == null ? "" : ((FinancialPk) value).dimChartaccount.getAccountKey() + "," +
((FinancialPk) value).dimCurrency.getCurrencyKey() + "," +
((FinancialPk) value).dimLocation.getLocationKey() + "," +
((FinancialPk) value).dimTime.getDimensionKey();
}

public void setAsText(String string) throws IllegalArgumentException {
FinancialPk f = new FinancialPk();
StringTokenizer token = new StringTokenizer(string);
f.setDimChartaccount(new DimChartaccount(Long.valueOf(token.nextToken(","))));
f.setDimCurrency(new DimCurrency(token.nextToken(",")));
f.setDimLocation(new DimLocation(token.nextToken(",")));
f.setDimTime(new DimTime(Long.valueOf(token.nextToken(","))));
if (f == null) {
throw new IllegalArgumentException("FinancialPk is null: " + string);
}
setValue(f);
}
}

And here's my initBinder:
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(FinancialPk.class, new FinancialPkSupport(FinancialPk.class));

SimpleDateFormat specFormat = new SimpleDateFormat(Constants.DATE_FORMAT);
binder.registerCustomEditor(Date.class, new CustomDateEditor(specFormat,true));

NumberFormat nf = NumberFormat.getNumberInstance(request.getLocale());
nf.setMaximumFractionDigits(Integer.parseInt(Constants.MAX_FRAX_DIGITS));
binder.registerCustomEditor(Double.class, null,new CustomNumberEditor(Double.class, nf, true));
binder.registerCustomEditor(Long.class, null,new CustomNumberEditor(Long.class, true));
}

And good bye frustating error!!

Tuesday, May 13, 2008

Generate hibernate composite-id using XDoclet

Few tables don't allowed to have a serial id just as modern table as in the appfuse by default. Ok, I mention one table of it: Financial, it has four PKs which are FK from four different tables: DimChartaccount, DimCurrency, DimLocation, and DimTime.

I've search the net and knew that it called composite-id (multiples id), and browse again to find how to generate its hbm directly from my pojo and got one link : Franklin Garcia's blog, it shows how to do it, but I've try it but unfortunately it fails to generate as I need, it cannot generate the composite-id part, but now I already fix it. here's how to do it, hope it helps:
  • name it still as id, but the type not as integer anymore, and no need generator class="increment", its type is PkFinancial. It's name must id because Ive try to name it with pkFinancial but I've got error fails in setter when I deploy it
  • PkFinancial is your own class, it must implements Serializable, and the properties are all of the PKs
/**
* @hibernate.class table="FINANCIAL"
*/
public class Financial extends BaseObject {
PkFinancial id; //must id, cannot name it as pkFinancial
Double saldoRupiah;
/**
* @hibernate.id name="id" class="jesperBlog.PkFinancial" //need to mention it's class
*/
public PkFinancial getId() {
return id;
}
public void setId(PkFinancial id) {
this.id = id;
}
/**
* @hibernate.property column="SALDO_RUPIAH" type="double" //usual property
*/
public Double getSaldoRupiah() {
return saldoRupiah;
}
public void setSaldoRupiah(Double saldoRupiah) {
this.saldoRupiah = saldoRupiah;
}
}

public class PkFinancial extends BaseObject { //must implements Serializable, BaseObject already done it
DimTime dimTime;
DimLocation dimLocation;
DimChartaccount dimChartaccount;
DimCurrency dimCurrency;

/**
* @hibernate.many-to-one name="dimTime"//just usual property, in my case it's many-to-one
* column="TIME_KEY" class="jesperBlog.DimTime"
*/
public DimTime getDimTime() {
return dimTime;
}
public void setDimTime(DimTime dimTime) {
this.dimTime = dimTime;
}
/**
* @hibernate.many-to-one name="dimLocation" column="LOCATION_KEY" class="jesperBlog.DimLocation"
*/
public DimLocation getDimLocation() {
return dimLocation;
}
public void setDimLocation(DimLocation dimLocation) {
this.dimLocation = dimLocation;
}
//oter getters setters
}

And the generated hbm looks like this:
...
<hibernate-mapping>
<class name="jesperBlog.Financial" table="FINANCIAL">

<composite-id name="id" class="jesperBlog.PkFinancial">
<key-many-to-one name="dimTime" class="jesperBlog.DimTime" column="TIME_KEY"/>
<key-many-to-one name="dimLocation" class="jesperBlog.DimLocation" column="LOCATION_KEY"/>
<key-many-to-one name="dimChartaccount" class="jesperBlog.DimChartaccount" column="ACCOUNT_KEY"/>
<key-many-to-one name="dimCurrency" class="jesperBlog.DimCurrency" column="CURRENCY_KEY"/>
</composite-id>

<property name="saldoRupiah" type="double" column="SALDO_RUPIAH"/>
...
</class>
</hibernate-mapping>