Saturday, June 25, 2005
Why use beanlib with Hibernate ?
Put simply, in cloning a Hibernate entity bean, we want to specify how deep we want to go in the object graph. Is it a 100% deep clone, a skin deep shallow clone, or some other combinations ? How about beans that involve a one-to-many collection ? The answer to this question has a significant impact on both the performance and memory footprint of the cloning process.
The JavaBean Library provides the following options
- 100% deep clone which will result in eagerly fetching all related entites and populating the values into pure POJO's which therefore will get rid of all CGLIB enhanced instances;
- Shallow clone which will result in cloning the top level bean and the immediately contained member fields of Java primitive types, String, Date, etc., but will exclude instances of Collection and application specific types. The excluded member fields will be set to null;
- Partial deep clone that extends only to a specified set of classes and a specified set of "collection properties". A collection property can be specified via a class named CollectionPropertyName. This allows an arbitrary portion of the object graph to be deeply cloned;
- Provide your own customized "Vetoer" by implementing the BeanPopulatable interface to control the population decision of each bean property;
- Provide your own customized BeanPopulatable and/or DetailedBeanPopulatable to completely override the population decision of each bean property.
- A new enhancement can be found here.
Reusable String Array in Spring's Application Context
A similar requirement is to create a single bean instance which is a java.util.Properties object in the application context. It turns out I can define a list instead of a string-array. It does not matter because spring can transform the list to a string-array if necessary. Example:<bean id="reusableStringArray" class="[Ljava.lang.String">
<list>
<value>a</value>
<value>b</value>
</list>
</bean>
Similarly one should be able to create a Properties instance via a map element. Thanks to Andreas on this.<bean id="strings" class="java.util.ArrayList">
<constructor-arg index="0">
<list>
<value>a</value>
<value>b</value>
</list>
</constructor-arg>
</bean>
Also, Carlos Sanchez has a nice blog on this subject.
http://static.springframework.org/spring/docs/1.2.x/reference/beans.html#d0e2331
Friday, June 24, 2005
Monkey business-sense
- if one over-promises and under-delivers, one runs the risk of loosing out in the long term;
- if one under-promises and over-delivers, one runs the risk of not getting the deal at all in the first place;
- if one delivers exactly as one promises, one still runs the risk of loosing out to (2) in the long term;
- To try to win a bid for the short term, pick over-promise and under-deliver;
- To delight the clients/consumers without actually putting in extra effort, pick under-promise and over-deliver;
- The most sustainable long term strategy, however, is deliver exactly as you promise.
Hibernate 3.0.5 Returns Corrupted Object via N-1 Navigation involving Subtypes ?
The mapping files:public abstract class Bar {
private long barId;
public long getBarId() { return barId; }
public void setBarId(long barId) { this.barId = barId; }
....
}
public class Bar1 extends Bar {...}
public class Bar2 extends Bar {...}
Now the HQL:<class name="Bar" table="BAR">
<id name="barId" type="long" column="BAR_ID">
<generator class="native" />
</id>
<discriminator column="TYPE" type="string" not-null="true" />
<subclass name="Bar1" discriminator-value="1" />
<subclass name="Bar2" discriminator-value="2" />
...
</class>
would return instances of either Bar1 or Bar2 depending on the discriminator value. This is all nice and cool.FROM Bar
However, say I have a FOO table with a foreign key to BAR:
Mapping File:public class Foo {
...
private Bar bar;
public Bar getBar() { return bar; }
public void setBar(Bar bar) { this.bar = bar; }
}
I would expect the object navigation foo.getBar() would return an instance of either Bar1 or Bar2, never Bar per se which is abstract and cannot be instantiated. e.g.<class name="Foo" table="FOO">
...
<many-to-one name="bar" class="Bar" update="false" insert="false" >
<column name="BAR_ID" />
</many-to-one>
...
</class>
The sad news is that in Hibernate 3.0.5 at least, the bar object from foo.getBar() turns out to be a CGLIB enhanced class based on Bar, not Bar1 nor Bar2. In other words, the returned object from foo.getBar() is corrupted!Foo foo = session.load(Foo.class, 1234);
Bar bar = foo.getBar();
The good news is one can get around the problem using eager fetching:
It's all nice and cool again.<class name="Foo" table="FOO">
...
<many-to-one name="bar" class="Bar" update="false" insert="false" lazy="false">
<column name="BAR_ID" />
</many-to-one>
...
</class>
Wednesday, June 22, 2005
Remove the Blogger's Header
Just add the following script to the bottom of your Template after the </body> and before the </html>:
Update in 2012: Apparently the template has been changed. So here is an update. Just add the following script right after the <body>:... </body> <script> var a = ["b-navbar", "header"] for (var i=0; i < a.length; i++) { var e = document.getElementById(a[i]) e.style.visibility="hidden" e.style.display="none" } </script> </html>
... <body> <script> var a = ["navbar-iframe-container"] for (var i=0; i < a.length; i++) { var e = document.getElementById(a[i]) e.style.visibility="hidden" e.style.display="none" removeChild(e); } </script>
DSL-300 ADSL Modem + DI-604 Router
Now in Australia I have a DSL-300 modem for ADSL internet access. DSL-300 can store at most one account configuration with a single MAC address. For direct connection to a PC, the MAC is automatically retrieved from the PC as at the time when the accunt configuration is created. If I need to switch to another PC, I actually need to delete the account configuration from the modem, and recreate a new account configuration via the new PC. Otherwise, the modem may show a connected status but I still will not be able to access the Internet.
To connect up my DI-604 router with the ADSL model, it gets a little bit more complicated. I need to
- Erase the existing account configuration from the DSL-300, if any;
- Set up my DI-604 with a WAN settings of PPPoE with my ISP username and password;
- Make sure that the WAN MAC Address used by the DI-604 is different from the MAC addresses of all the PC's connected to it.
Tuesday, June 21, 2005
Oracle Blob mapped to byte[] in Hibernate
As found in the Hibernate forum, there is a problem of mapping a byte[] JavaBean property to "binary" in the Hibernate mapping file. Basically the byte array can be written to Oracle, but what's read back from Oracle is always 86 bytes which is the Oracle Blob locator itself!
Here is one solution I've found to get around this problem, assuming, say, the byte[] property name is "image",
- Keep an internal member field for the byte[] image, with the standard {get|set}Image methods;
- Add a pair of {get|set}ImageBlob methods for getting and setting the Blob datatype, without the repsective Blob member field. This pair of methods are intended to be invoked only by Hibernate;
- Change the property mapping from "image" to "imageBlob", and use "blob" instead of "binary" for the type in the Hibernate mapping file.
Note, with this approach, you may need to use the Oracle 10.1.0.4+ JDBC thin driver regardless to get around a problem related to the blob size greater than 2K bytes (even if you are running the Oracle 9.2.0.x database). Apparently it's related to a bug in the 9.2.0.x JDBC driver.
Example:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.sql.Blob;
import java.sql.SQLException;
import org.hibernate.Hibernate;
public class Foo implements Serializable {
byte[] image;
public byte[] getImage() {
return image;
}
public void setImage(byte[] image) {
this.image = image;
}
/** Don't invoke this. Used by Hibernate only. */
public void setImageBlob(Blob imageBlob) {
this.image = this.toByteArray(imageBlob);
}
/** Don't invoke this. Used by Hibernate only. */
public Blob getImageBlob() {
return Hibernate.createBlob(this.image);
}
private byte[] toByteArray(Blob fromBlob) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
return toByteArrayImpl(fromBlob, baos);
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (baos != null) {
try {
baos.close();
} catch (IOException ex) {
}
}
}
}
private byte[] toByteArrayImpl(Blob fromBlob, ByteArrayOutputStream baos)
throws SQLException, IOException {
byte[] buf = new byte[4000];
InputStream is = fromBlob.getBinaryStream();
try {
for (;;) {
int dataSize = is.read(buf);
if (dataSize == -1)
break;
baos.write(buf, 0, dataSize);
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
}
}
}
return baos.toByteArray();
}
}
Sample property mapping:
<property name="imageBlob" type="blob" column="IMAGE" />
Saturday, June 18, 2005
Generic Deep Clone via XStream
To remove this constraint, we can apply similar technique with XStream, which doesn't require a class to implement Serializable for serializing deeply to/from XML. The deep clone would then work for all classes. (Note XStream does not serialize transient fields.)
Friday, June 17, 2005
Nice fonts for IDE
Wednesday, June 15, 2005
Controlling layout using CSS
Such technique can greatly simplify the page content. Bye bye to the nested tables within tables!
Monday, June 06, 2005
Integrating Jakarta Commons Logging with Websphere 5.x
Anyhow, at least for development purposes, adding the system property:
-Dorg.apache.commons.logging.LogFactory=will force Websphere to use the normal commons logging implementation instead of it's own.
org.apache.commons.logging.impl.LogFactoryImpl
log4j and WSAD
Also, to configure log4j properly for EJB project in WSAD 5.x, not only does log4j.xml need to be in the Java Build Path of the EJB project, the folder containing the log4j.xml must also be under the EJB project. Interestingly this is not necessary for a non-EJB project.
For example, for a non-EJB project PA, I can reuse an existing folder PB/F containing log4j.xml of a different project PB by setting the folder PB/F as a class folder of PA. This works if and only if PA is a non-EJB project.
Just realized a better alternative to the build path approach is to set up an independent Java project with a log4j folder as a class folder in the Java project's build path. Include the Java project as part of the EAR of the EJB project, and set the jar file dependency of the EJB project to depend on the Java project.