Hibernate + Spring : cascade deletion ignoring non-nullable constraints
- by E.Benoît
Hello,
I seem to be having one weird problem with some Hibernate data classes. In a very specific case, deleting an object should fail due to existing, non-nullable relations - however it does not. The strangest part is that a few other classes related to the same definition behave appropriately.
I'm using HSQLDB 1.8.0.10, Hibernate 3.5.0 (final) and Spring 3.0.2. The Hibernate properties are set so that batch updates are disabled.
The class whose instances are being deleted is:
@Entity( name = "users.Credentials" )
@Table( name = "credentials" , schema = "users" )
public class Credentials
extends ModelBase
{
private static final long serialVersionUID = 1L;
/* Some basic fields here */
/** Administrator credentials, if any */
@OneToOne( mappedBy = "credentials" , fetch = FetchType.LAZY )
public AdminCredentials adminCredentials;
/** Active account data */
@OneToOne( mappedBy = "credentials" , fetch = FetchType.LAZY )
public Account activeAccount;
/* Some more reverse relations here */
}
(ModelBase is a class that simply declares a Long field named "id" as being automatically generated)
The Account class, which is one for which constraints work, looks like this:
@Entity( name = "users.Account" )
@Table( name = "accounts" , schema = "users" )
public class Account
extends ModelBase
{
private static final long serialVersionUID = 1L;
/** Credentials the account is linked to */
@OneToOne( optional = false )
@JoinColumn( name = "credentials_id" , referencedColumnName = "id" , nullable = false , updatable = false )
public Credentials credentials;
/* Some more fields here */
}
And here is the AdminCredentials class, for which the constraints are ignored.
@Entity( name = "admin.Credentials" )
@Table( name = "admin_credentials" , schema = "admin" )
public class AdminCredentials
extends ModelBase
{
private static final long serialVersionUID = 1L;
/** Credentials linked with an administrative account */
@OneToOne( optional = false )
@JoinColumn( name = "credentials_id" , referencedColumnName = "id" , nullable = false , updatable = false )
public Credentials credentials;
/* Some more fields here */
}
The code that attempts to delete the Credentials instances is:
try {
if ( account.validationKey != null ) {
this.hTemplate.delete( account.validationKey );
}
this.hTemplate.delete( account.languageSetting );
this.hTemplate.delete( account );
} catch ( DataIntegrityViolationException e ) {
return false;
}
Where hTemplate is a HibernateTemplate instance provided by Spring, its flush mode having been set to EAGER.
In the conditions shown above, the deletion will fail if there is an Account instance that refers to the Credentials instance being deleted, which is the expected behaviour. However, an AdminCredentials instance will be ignored, the deletion will succeed, leaving an invalid AdminCredentials instance behind (trying to refresh that instance causes an error because the Credentials instance no longer exists).
I have tried moving the AdminCredentials table from the admin DB schema to the users DB schema. Strangely enough, a deletion-related error is then triggered, but not in the deletion code - it is triggered at the next query involving the table, seemingly ignoring the flush mode setting.
I've been trying to understand this for hours and I must admit I'm just as clueless now as I was then.