Showing posts with label hibernate. Show all posts
Showing posts with label hibernate. Show all posts

Tuesday, January 12, 2010

The Curious Case of Damned DataIntegrityViolationException

In one of the projects on which I contract, we recently started encountering a problem importing and parsing text record files into our system which previously had no problem. My first thought on hearing this was that the partner from which we obtain the files had changed the file format (again). Upon closer inspection, nothing had changed in the files. My next step was to try importing them into a development system and seeing what was going on. As it turned out, the application was catching Spring's DataIntegrityViolationException. I was floored as soon as I saw this because our application was supposed to be catching this exception behind one of the business interfaces and converting it to an internal exception which is used in business logic. After some more poking around to confirm what was really going on, I threw the problem into google, and on the second result was a post in the Spring forum made by a user having exactly the same problem I was.


To summarize their problem quickly, they were using a transaction manager, and they were intercepting their business methods (via interfaces) with Aspects, which we're also doing. The problem was this : as soon as the internal Aspects were applied to the business interface, this changed the ordering of advice applied to the interface implementor, so now the Hibernate session underneath was getting flushed later by the transaction manager, instead of in the business method where it had been flushed previously. The result of this was that now DataIntegrityViolationExceptions were being thrown outside of the interecepted method, instead of inside where it was expected. A manual session.flush() inside of a HibernateCallback within the business method fixed this :


/**
* @see AchPaymentNoticeOfChangeService#registerNoc(AchPaymentNoticeOfChange)
*/
@Override
public void registerNoc(final AchPaymentNoticeOfChange changeNotification) throws IllegalArgumentException, NoticeOfChangeAlreadyExistsException, Exception {
try {
getHibernateTemplate().execute(new HibernateCallback() {
@Override
public Object doInHibernate(Session session) throws HibernateException, SQLException {

session.save (changeNotification);

// flush the session to ensure that the database gets synchronized
// the end of this call, rather than waiting for any transaction
// managers to handle it and risk letting a DataIntegrityViolationException
// occur outside of this method's handling
session.flush();

return null;
}
});
} catch (DataIntegrityViolationException dive) {
throw new NoticeOfChangeAlreadyExistsException("A notice of change already exists for payment ["+changeNotification.getAchPayment().getId()+"]", dive, changeNotification);
} catch (Exception e) {
throw e;
}
}


I hope this post finds somebody else who runs into this problem.

Wednesday, March 25, 2009

More Hibernate glitches, part 2

I had previously written on an Expected positional parameter glitch in Hibernate when joining between entities using OneToOne and PrimaryKey + ForeignKey columns. It turns out, Hibernate has this problem no matter what field you query on the joined entity if you're quering by another entity. A way around this is to use the ID property of the joined entities and add another association to your HQL / Criteria query.

ie So if you have Person and Address having a OneToOne relationship


public class Person {
@OneToOne
private Address address;
}


... and you want to query for a Person by Address, you would probably write something like :

from Person p where p.address = ?


... in HQL. Well, this is where you start running into the Expected positional parameter glitch. Instead, write your query like this :

from Person p where p.address.id = ?


... and pass in a long / int / whatever type you use for an identifier for Person / Address instead and this will work around the glitch until it can be fixed.

Thursday, March 12, 2009

More Hibernate glitches

I've recently discovered the joy of actually getting OneToOne primary key columns on objects working, so I've started using them more often (where appropriate). Naturally, increased use means increased chances of finding a bug, which I have. It seems there's a parser problem in HQL when it comes to querying on ID columns that are also objects. If you try to query on the object type (ie Person) rather than the ID type (ie Long), you'll get an exception saying something along the lines of :


Expected positional parameter count: 1, actual parameters: [Parent@bec357b] [from Child this where this.id.parent = ?]


The above line is from HHH-2254, but it's representative of the problem I ran across. The problem is that hibernate doesn't properly parse and fill in the parameters in the query. You can get around this by changing the query to use a simple type for ID instead of the object type.

Friday, November 21, 2008

On Hibernate UserTypes

Hibernate UserTypes are a great way for persisting complex classes which may require more than one column in a table in order to be properly represented in a database. Many people have used them successfully in order to be able to simplify storage of complex objects and thus simplify their applications at the same time. One simpler use case of them was to create Enum types before support for Java 5+ enums came along.
Now, you'd think that once you've got Java 5 support with Hibernate, you'd never have to use such a simple use case again because you've got the Enums themselves, along with the @Enumerated annotation you can place on fields and properties, right ? Wrong. I've recently started migrating my projects to Spring Security from Acegi Security, and I've found that in the process, they've made the GrantedAuthority interface extend Comparable. This conflicts with the fact that Java 5+ Enums already implemented the Comparable interface with a generic parameter, and as such your code won't compile if you have an Enum that also implements GrantedAuthority, such as the following :


public enum Role implements GrantedAuthority, ConfigAttribute
{
ROLE_ADMINISTRATOR;

public String getAuthority() {
return name();
}

public String getAttribute() {
return name();
}

public String getMessageKey() {
return "enum.Role.".concat(name());
}
}


In order to remedy the conflict, I've had to convert my nice, pretty, simple Enum into a class, and simulate Java 5+'s enum behaviour, like so :


public final class Role implements GrantedAuthority, ConfigAttribute, Serializable, Cloneable {

private static final long serialVersionUID = 1L;

public static final Role ROLE_ADMINISTRATOR = new Role(0, "ROLE_ADMINISTRATOR");

private static final Role[] VALUES = new Role[] {
ROLE_ADMINISTRATOR
};

private static final Map NAME_MAPPINGS = new HashMap();

static {
Arrays.sort(VALUES, new Comparator() {
public int compare(Role o1, Role o2) {
return o1.ordinal - o2.ordinal;
}
});

for (Role r : VALUES) {
NAME_MAPPINGS.put (r.getName(), r);
}
}

private int ordinal;
private String name;

//Constructors

/*
* DO NOT EVER USE THIS! It exists only for serialization purposes.
*/
public Role() {
super();
}

private Role(Role r) {
this(r.ordinal, r.name);
}

private Role(int ordinal, String name) {
super();
this.ordinal = ordinal;
this.name = name;
}

//Behaviour Methods

public int compareTo(Object o) {

if (o == null || o.getClass() != Role.class) {
throw new IllegalArgumentException(
"Comparison object may not be null, and must be a Role");
}

return this.ordinal - ((Role) o).ordinal;
}

public String getAuthority() {
return getName();
}

public String getAttribute() {
return getName();
}

//Pseudo-properties

public String getMessageKey() {
return "enum.Role.".concat(getName());
}

//Property Accessors

public final int getOrdinal() {
return this.ordinal;
}

public final String getName() {
return this.name;
}

//Helper Methods

public static final int hashCode(Role r) {
return r == null ? 0 : r.hashCode();
}

public static final boolean equals(Role x, Role y) {
return x == null ? (y == null) : x.equals(y);
}

public static final Role clone(Role r) {
return r == null ? null : r.clone();
}

public static final Role[] values() {
return VALUES;
}

public static final Role valueOf(String s) throws IllegalArgumentException {

String key = defaultString(s).toUpperCase();

if (NAME_MAPPINGS.containsKey(key)) {
return NAME_MAPPINGS.get(key);
} else {
throw new IllegalArgumentException("No role by the name ["+s+"] exists");
}
}

//Object Overrides

@Override
public String toString() {
return new StringBuffer().append(this.name)
.append("(").append(this.ordinal).append(")")
.toString();
}

@Override
public Role clone() {
return new Role(this);
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
result = prime * result + this.ordinal;
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Role other = (Role) obj;
if (this.name == null) {
if (other.name != null)
return false;
} else if (!this.name.equals(other.name))
return false;
if (this.ordinal != other.ordinal)
return false;
return true;
}

}


Once this was done, I created a Hibernate UserType implementation for Role, so that Hibernate could persist Roles as Integers, just as was already being done for the Enum version of Role:


public class RoleUserType implements UserType {

private static final int[] SQL_TYPES = new int[] { Types.INTEGER };

public static final String HIBERNATE_TYPE_NAME = "RoleUserType";

public Object deepCopy(Object value) throws HibernateException {
return value;
}

public Object assemble(Serializable cached, Object owner) throws HibernateException {
return (Role)cached;
}

public Serializable disassemble(Object value) throws HibernateException {
return (Role)value;
}

public boolean equals(Object x, Object y) throws HibernateException {
return Role.equals((Role)x, (Role)y);
}

public int hashCode(Object x) throws HibernateException {
return Role.hashCode((Role)x);
}

public boolean isMutable() {
return false;
}

public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {
int roleOrdinal = resultSet.getInt(names[0]);

return resultSet.wasNull() ? null : Role.values()[roleOrdinal];
}

public void nullSafeSet(PreparedStatement statement, Object value, int index)
throws HibernateException, SQLException {
if (value == null) {
statement.setNull(index, Types.INTEGER);
} else {
statement.setInt(index, ((Role)value).getOrdinal());
}
}

public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}

@SuppressWarnings("unchecked")
public Class returnedClass() {
return Role.class;
}

public int[] sqlTypes() {
return SQL_TYPES;
}

}


Once the Hibernate UserType implementation was written, it was just a matter of adding my model package to Hibernate's list of annotated packages (using Java 5 after all) :


@TypeDef(name = RoleUserType.HIBERNATE_TYPE_NAME, typeClass = RoleUserType.class)
package com.mypackage.model;

import org.hibernate.annotations.TypeDef;
import eu.alenislimited.acshelper.support.hibernate.RoleUserType;


After defining the Hibernate UserType type, it was then just a matter of going around to all my fields that used Role, and replacing ...


@Enumerated(EnumType.ORDINAL)


... with ...


@Type(type = RoleUserType.HIBERNATE_TYPE_NAME)


Fortunately, doing the conversion was just extra work, and didn't require any special changes to any of my Spring configuration, or even my Hibernate configuration (beyond adding the @TypeDef to my model package and adding it to my Hibernate SessionFactory's list of annotatedPackages).

Thursday, June 05, 2008

JAXB schemagen - Finally got it working

For those not in the know, schemagen is a program that comes as part of the Java Web Services Developer Pack and it's used to generated XML schemas from JAXB annotations on Java beans. It so happens that the creators of the JAXB reference implementation also made a Maven 2 plugin for this program. I had previously attempted to use it to generate schemas in my initial attempts with JAXB, but had been presented with a slough of unfriendly exceptions being thrown at me whenever I ran the plugin. Not having the time back then to really play around with it and being considerably less experienced with JAXB, I had to shelve it and find other, less satisfying solutions to my problems with generating documentation.


Recently, I've had to come back to using JAXB because I'm making a RESTful Web Services API that leverages the power of both JAXB and Hibernate to do all my heavy lifting for me. This time around, I wasn't willing to tolerate a lack of schema to give to our users of the API, because it would mean a lot more work for them, and a lot more work for me. This time, I decided to be persistent and dig around the jaxb-schemagen plugin to make sure I could get it working. I'm proud to say that my perseverance paid off, and I now have my RESTful API schema being automatically generated for my application. Here are the problems and the solutions I ran into when I was trying to get going on this:



NullPointerException

I came across persistent NullPointerExceptions while trying to run the plugin at first. The stack trace indicated that the problem was with apt, the Java annotation processing tool. I later discovered that in order to properly map your classes into XSD schema types, you must specify an @XmlType annotation on *all* of the classes you wish to map (at least with JAXB 2.0, which is the version I'm currently using)

ClassCastException

After I resolved the first problem, I came across another pesky exception that likewise did not provide any useful debugging information. The plugin output indicated that it was finding annotations that it didn't know how to deal with, specifically the Java Persistence API annotations that I was using for Hibernate. After much digging around on Google and some detective work, I discovered that any annotations encountered by the jaxb-schemagen plugin had to be on the Maven runtime classpath. I double checked my POM to find that I had specified the scope of the JPA / Hibernate annotations as 'provided', which instructs Maven not to load them into either the compile or runtime classpaths. The initial reason I had for specifying the dependency scope as 'provided' was so that they would not needlessly get included with my project WAR files. I changed their scope to 'compile' (which also includes 'runtime') and voila, apt could now properly detect the unknown annotations, and get past them to deal with the JAXB annotations it needs to create the XML schema.



If anybody else encounters similar errors with JAXB-schemagen, I hope they find this page and find it useful.

Monday, May 26, 2008

RESTful web services with JAXB 2.0

In case you're not familiar, JAXB (Java API for Xml Binding) is an API developed by Sun for binding between XML and Java beans. The first incarnation, 1.x, was interface+implementation based like XMLBeans from the Apache Project. The second incarnation, 2.x, is annotation+pojo based. It's a very useful API, especially when combining it with Hibernate (and the Java Persistence API) to make restful web services. But, that's not really the point of this post. The point is that I've been using JAXB 2.0 for web services much in the past, but this is the first time where I've had to map the "class hierarchy per-table" pattern used by Hibernate into XML. As it turns out, JAXB 2 has very good support for this pattern. You can read more on this at the java.net project page for JAXB.

Thursday, May 08, 2008

Logging for tests using the Spring Framework

It's been a while since I've posted to the blog 'cause I've been so busy, and it seems fitting that this be a good way to resume posting, as this issue has pissed me off quite a bit and been a major thorn in my side for the longest time.


The Spring Framework has some pretty good support for creating test classes for your application, however it by default does not properly initialize log4j logging when doing tests, and I found out today why. When running your application in a Servlet container, you'd configure Spring logging in web.xml. However, when running in a standalone context, Spring has no way of knowing how you want logging configured, so it leaves it up to log4j to configure itself. On that front, you have to realize what log4j's default configuration strategy is : reading a 'log4j.properties' file from the root of the classpath. Once this hits you (and it took me a while), getting logging running for your test cases becomes a simple matter of placing a valid 'log4j.properties' config file in the root of your test classpath, and logging starts working properly, so now you can read those pesky hibernate generated queries off your test log .

Thursday, December 20, 2007

Cool MySQL tricks

It's been a while since I've written a blog post, and I figured this was a good topic to write about. For the most part, I don't use SQL. There's numerous ORM (Object-Relational Mapping) tools and frameworks out there (ie Hibernate) that abstract away all the boiler-plate CRUD code. When I do have to use SQL (be it on the command line or with a query browser), I'm not afraid to dive right in, and it's interesting to learn new things about SQL because there's so much to learn and it's such a ridiculously powerful query language.

I had to update the configuration in one of my databases, but the configuration required inserting numerous new lines into a table for user roles. I could've written a stored procedure to do it, but given my (admittedly somewhat limited) knowledge of SQL, I figured this would take too much time and I didn't want to do it. (This is what I like to call constructive laziness). I kept Googling and found MySQL's INSERT INTO ... SELECT syntax, which allows you to conveniently insert the results of one query into another table. I'm sure that most other database implementations out there have a similar convenience syntax, but I happen to work with MySQL so there you go. It took what would've been a numerous line script with looping and separate SELECT and INSERT statements down to a one liner. If you use MySQL (especially with LAMP / AJAX) I recommend that you read about it.

Monday, October 15, 2007

Moar Hibernate !!!

Yet again, I'm posting because of fucking hibernate. One of the old quirks I had run across was that if you had a setter for a collection, ie :

public void setItems(List items)
{
this.items = items;
}

...this would replace the hibernate-backed collection that already existed (if one did) and could replace it with a non-hibernate implementation such as java.util.Vector, and any cached items would not get properly dealt with, and the collection would not get properly persisted, even to the point of throwing an exception. This resulted in me having to change my setters to this :

public void setItems(List items)
{
if(this.items == null)
{
this.items = items;
} else
{
this.items.clear();
this.items.addAll(items);
}
}

The only problem with this is that depending on the scenario, hibernate gets the collection, and then sets exactly the same list object back into the persistent entity we're dealing with, which would result in clearing exactly the same list we're trying to assign. The remedy :

public void setItems(List items)
{
if(this.items == null)
{
this.items = items;
} else if(this.items != items) //fix: identity check the two lists!
{
this.items.clear();
this.items.addAll(items);
}
}

That really should have been there anyway, but hibernate inspired it. Fuck you hibernate.

Tuesday, September 11, 2007

<rage>MOAR HIBERNATE! :@</rage>

God damn, I'm getting so fucking tired of Hibernate and its quirks. Here's a new one regarding Criteria queries :

When you create a criteria query, you almost invariably have to specify a result transformer of Criterion.DISTINCT_ROOT_ENTITY (that's a quirk, but not the topic of this post). When you have an entity on which you want to build a criteria query and you want to limit the number of search results (ie distinct root entities), things get really tricky. Specifying 'setMaxResults' on the query affects the number of rows returned from the database that are actually inspected. Therefore, if there are any joins on your entity that have collections, this will cause a fetch with an outer join strategy to generate an excessive number of rows and affect the results when using a maxResults setting. In the case of using embedded properties, specifying a fetch mode of SELECT will not override these (this is a glitch in hibernate). You'll have to specify the fetch mode manually in the metadata (be it XML or Annotations) permanently for the embedded class. I fucking hate Hibernate sometimes, I really do.

Wednesday, August 22, 2007

Moar (sic) hibernate !

Yet more hibernate fun facts that I wish I'd had before I started any projects : Hibernate has the ability to persist instances of java.util.Locale, java.util.Currency, and java.util.TimeZone. A sprinkling of a few bits of these pieces of code and their corresponding hibernate annotations could have saved me an assload of time spent on doing my own design, development and testing.

On a small side note, I found I've actually managed to come up with a much better and more appropriate solution for dealing with currencies than the java.util.Currency class, as it's woefully inadequate to the task of dealing with currencies in an enterprise system. I refer specifically to the way it deals with floating point arithmetic in its number of decimal places, rather than adering more closely to the ISO 4217 specification whereby currencies should have a base and an exponent when calculating a multiplier to go between base currency units and major currency units.