Migrating from JBoss to Glassfish
I recently migrated a J2EE application from JBoss 4.0.4-GA to Glassfish and experienced a number of different issues.
In this post I will describe the issues and the solutions to them in the hopes that it will help solve others headaches.
Issue #1 JBoss EJBQL != Glassfish(J2EE Spec) EJBQL
Take for example the following EJBQL that will function under JBoss:
“select object(s) from Slab as s where s.branch =”+branch;
Does not work properly under Glassfish unless (I believe, but not tested) you @Override the equals(Object) method
in your persistent entities.
A simple work around for those of us that are LAZY and do not feel like @Overriding all of those equals(Object) methods is to change your EJBQL to something like the following:
“select object(s) from Slab as s where s.branch.id =”+branch.getId();
Issue #2 Embeddable objects some how get referenced to Entities in the Glassfish Cache
To be honest I think this is a horrible feature of Glassfish but by no means am I going to spend the time to try
to change the Glassfish developer communities mind on it (And it could just be J2EE spec).
Take for instance the following Object Model:
1 2 3 4 5 6 7 8 9 10 11 | @Entity public class Customer implements Serializable { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; @Column(name="c_name") private String name; @Embedded private Address address; //Magical get/set methods here } |
1 2 3 4 5 6 7 8 9 10 | @Embeddable public class Address implements Serializable { private String streetLineOne; private String streetLineTwo; private String city; private String state; private String postalCode; private String country; //Magical get/set methods here } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | @Entity @Table(name="belt_order_header") public class OrderHeader implements Serializable { public enum OrderStatus { BID, ORDER, INVOICE, HOLD, CONSIGNMENT } @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; private OrderStatus status; private String poNumber; private String internalNotes; private ShipVia shipVia; @Embedded @AttributeOverrides( { //Magical Overrides } ) private Address billTo; @Embedded @AttributeOverrides( { //Magical Overrides } ) private Address shipTo; @ManyToOne private Customer customer; @ManyToOne private Branch billingBranch; @ManyToOne private Branch fabricationBranch; //Magical get/set methods here } |
If I were to try the following in a Session Bean:
1 2 3 4 5 6 7 | public OrderHeader createOrderHeader(Customer customer) { OrderHeader oh = new OrderHeader(); oh.setBillTo(customer.getAddress()); oh.setShipTo(customer.getAddress()); em.persist(oh); return oh; } |
It would throw some narly unreadable and annoying Exception. The reason you ask? It happens because
the PersistentContext has associated the Address object to the Customer.
The work around is to create in the Address object a static method that copies the attributes
from one Address object to a new one. Like the following:
1 2 3 4 5 6 7 8 9 10 | public static Address copy(final Address address) { Address copy = new Address(); copy.setStreetLineOne(address.getStreetLineOne()); copy.setStreetLineTwo(address.getStreetLineTwo()); copy.setCity(address.getCity()); copy.setState(address.getState()); copy.setCountry(address.getCountry()); copy.setPostalCode(address.getPostalCode()); return copy; } |
And redo the OrderHeader method as such:
1 2 3 4 5 6 7 | public OrderHeader createOrderHeader(Customer customer) { OrderHeader oh = new OrderHeader(); oh.setBillTo(Address.copy(customer.getAddress())); oh.setShipTo(Address.copy(customer.getAddress())); em.persist(oh); return oh; } |
Issue #3 Once again the GlassFish cache
Since there is a great article on this I will just link it. Just remember REFRESH, REFRESH, REFRESH!
http://weblogs.java.net/blog/guruwons/archive/2006/09/understanding_t_1.html
Here is a example of what I mean:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public BeltType find(Object pk) { BeltType beltType = (BeltType) em.find(BeltType.class, pk); try { em.refresh(beltType); } catch(EntityNotFoundException ex) { return null; } if(beltType == null) return null; loadLazyInitializations(beltType); return beltType; } |
I’ve done a spotlight on your entry at TheAquarium. Posted for this evening. I’ll also tell Sekhar to send you an email so you can help other peole migrating. - eduard/o
Comment by Eduardo Pelegri-Llopart — March 23, 2008 @ 11:59 am
Your code is unreadable (because unindented).
S.
Comment by Stefane Fermigier — March 24, 2008 @ 1:49 am
Sorry for the issue. I believe my last few edits should resolve your code readability issue.
Comment by admin — March 24, 2008 @ 8:51 am
David, thanks for sharing your migration experience. These would be great additions to the GlassFish migration guide http://wiki.glassfish.java.net/Wiki.jsp?page=M2GMigrationGuide . The guide is intended to help people with migrating to GlassFish ( see announcement http://forums.java.net/jive/thread.jspa?messageID=261465 ). So,I created links to the three issues in this blog from the migration guide. It would be very helpful if you could contribute your code example/text inline in the migration guide. It is in community based wiki format. So you can edit yourself. If you have any questions, please email at sekhar@dev.java.net. Thanks !
Also, on issue 3, what is being done differently on JBoss ?
Comment by Sekhar Vajjhala — March 24, 2008 @ 10:04 am
Sekhar,
Thank you for your post. If I have time I will contribute it to the Wiki but it might be a month or 2 since I am up againest a deadline.
On Issue #3
In JBoss you would not have to em.refresh(object). It appears that there is no caching used on JBoss by default. On em.find(class,object) would actually pull the data from the DB.
Comment by admin — March 24, 2008 @ 10:18 am
Hi David,
Thanks for this great overview.
Using NetBeans to develop JEE applications, Entities are generated by default with the equals method overridden. This is of little use when migrating an existing project from JBoss to GlassFish, but for new projects that are developed for GlassFish this might be a solution.
Greets, Wouter
Comment by Wouter van Reeven — March 25, 2008 @ 6:25 am
Thanks Wouter for the post,
The comment I made about override must be taken in context of it being un-tested.
Meaning that I am blaming my issue on not having equals(Object) @Override but have
not verified that it is actually causing my issue. What I do know, is that I have a work around.
But now that you have me on my “Soap Box”.
Generally overriding equals(Object) and hashcode() methods are my biggest annoyance
with J2EE spec. Not all “Entities” can have business keys that make them equal because
certain business rules prevent it even if all the data attributes are equal. Since I am a
bit of a neanderthal this whole idea of overriding those methods has never made sense.
Please some write me a comment on how to do this properly so I myself can move on
from the stone age.
Comment by admin — March 25, 2008 @ 6:41 am