Just listen to Alex

December 22, 2008

Finding out exactly which entities are causing a ConstraintViolationException (JPA)

Filed under: programming — Tags: , , — bosmeeuw @ 8:27 pm

Say you have an application using JPA. Your users can use and administrate lists of entities. They probably have the ability to permanently delete entities from the database.

If the entity the user wants to delete is used on another entity, the database should throw you a nice ConstraintViolationException, which JPA will (curiously) wrap in an EntityExistsException. You can catch this exception and display a nice informational message saying “You can’t delete this item because it’s already been used!”. This is better than just showing them the error the database spat out.

But what if you have a many entities, which have many relations between eachother? The user might want to find out just where their item is being used. And you might not feel like writing code to scan all your tables to find the item refering to the entity that’s being deleted. Below, I will explain how you can find out which entities are blocking the deletion of an arbitrary entity using some Reflection and ClassPath scanning.

Let’s say you have a service method like this:

public void deleteUser(long id) {
	User entity = em.find(User.class, id);

First, you’ll need to catch the Runtime Exception JPA will throw when encountering a constraint violation. Your service method will throw a YourApplicationException, which you will handle upstream and display as a nice error message.

public void deleteUser(long id) throws YourApplicationException {
	try {
		User entity = em.find(User.class, id);
	catch (EntityExistsException e) {
		throw new YourApplicationException("You tried to delete an item which is in use.", e);

To find out which entities are refering to the item we are deleting, we will inspect the database error message and extract the table name of the refering entity. Note the datbase message is specific to the DBMS you are using. I’m using PostgreSQL, if you are using a different DBMS you will probably need to tweak the pattern to match the message your DBMS is spitting out. Once we have the tablename, we will match it to an entity class by scanning the classpath. After that, we fill find out which field on the target class is of the same type as the entity being deleted (using reflection). We then make a query using JPA to fetch the referening entities. We put these entities in the exception, so it can be caught upstream and the entities can be displayed in a list.

Here’s the code for all of this:

public void delete(long id) throws YourConstraintViolationException {
	try {
		User entity = em.find(User.class, id);
	catch (EntityExistsException e) {
		//need to rollback the transaction because we'll be doing a query later on
		List<Object> linkedEntities = null;
		linkedEntities = findLinkedEntitiesFromContraintViolation(User.class, id, (ConstraintViolationException) e.getCause());
		throw new YourConstraintViolationException(linkedEntities, e);

private List<Object> findLinkedEntitiesFromContraintViolation(Class<?> deletedEntityClass, long deletedEntityId, ConstraintViolationException e) {
	//unravel the exception so we have the SQLException
	BatchUpdateException batchUpdateException = (BatchUpdateException) e.getCause();
	SQLException sqlException = batchUpdateException.getNextException();
	List<Object> entities = new ArrayList<Object>();
	//match the database error message to find out the refering table name
	Matcher matcher = Pattern.compile("referenced from table \"(.*?)\"").matcher(sqlException.getMessage());
	if(matcher.find()) {
		String tableName = matcher.group(1);
		//we need to specify the base package all our entities reside under (possibly in sub-packages), and pass the ClassLoader of one of the entity classes to make the classpath scanning easier
		Class<?> entityClass = ClassUtils.findClassByCaseInsensitiveName(User.class.getClassLoader(), "your.entities.package",tableName);
		//check out which fields could possibly refer to the deleted class
		for(Field field : ClassUtils.getAllDeclaredFields(entityClass)) {
			if(field.getType().isAssignableFrom(deletedEntityClass)) {
				//fetch the refering entities using a JPA query and add them to the result
				String query = "FROM " + entityClass.getSimpleName() + " obj WHERE obj." + field.getName() + ".id = :deleted_id";
				List resultList = em.createQuery(query).setParameter("deleted_id",deletedEntityId).setMaxResults(10).getResultList();
	return entities;

The YourConstraintViolationException could look something like this:

public class YourConstraintViolationException extends Exception {
    private List<Object> linkedEntities;

    public YourConstraintViolationException(List<Object> linkedEntities, Throwable cause) {
        this.linkedEntities = linkedEntities;

    public List<Object> getLinkedEntities() {
        return linkedEntities;


The code calling your service method might look this this:

try {
catch(YourConstraintViolationException e) {
	out.write("<b>There was an error deleting the user. The user is in use on these items:</b>");
	for(Object linkedEntity : e.getLinkedEntities()) {
		out.write("<li>" + linkedEntity.toString() + "</li>");

The actual classpath scanning is going on inside ClassUtils. This class will do its work both when your entities are normal .class files on disk, and when they’re packaged inside a jar. Here’s the code for ClassUtils.java:

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ClassUtils {

    public static Class<?> findClassByCaseInsensitiveName(ClassLoader classLoader, String basePackage, String className) throws ClassNotFoundException, IOException, URISyntaxException {
	    URL packageUrl = classLoader.getResource(basePackage.replace(".","/"));
	    Matcher matcher = Pattern.compile("(file:/.*?.jar)!(.*)").matcher(packageUrl.getFile());
	    if(matcher.find()) {
	        String jarFileUrl = matcher.group(1);
	        return findClassByCaseInsentiveNameInJar(basePackage, new File(new URI(jarFileUrl)), className);
	    else {
    	    File packageFolder = new File(packageUrl.getFile());
    	    return findClassByCaseInsentiveNameInDirectory(basePackage, packageFolder, className);

    private static Class<?> findClassByCaseInsentiveNameInJar(String basePackage, File jarFilePath, String className) throws IOException, ClassNotFoundException {
        JarFile jarFile = new JarFile(jarFilePath);
        String packagePath = basePackage.replace(".","/");
        String patternString = Pattern.quote(packagePath) + ".*/" + className + "\\.class";
        Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
        ArrayList<JarEntry> entries = Collections.list(jarFile.entries());
        for(JarEntry entry : entries) {
            Matcher matcher = pattern.matcher(entry.getName());
            if(matcher.matches()) {
                String fullClassName = entry.getName().replace("/",".").replace(".class","");
                return Class.forName(fullClassName);
        return null;

    private static Class<?> findClassByCaseInsentiveNameInDirectory(String packageName, File packageFolder, String className) throws ClassNotFoundException {
        for(File file : packageFolder.listFiles()) {

            if(file.getName().toLowerCase().equals(className + ".class")) {
                String fullClassName = packageName + "." + file.getName().replace(".class","");
                return Class.forName(fullClassName);
            if(file.isDirectory()) {
                String subPackageName = packageName + "." + file.getName();
                Class<?> foundClass = findClassByCaseInsentiveNameInDirectory(subPackageName, file, className);
                if(foundClass != null) {
                    return foundClass;
        return null;

    public static List<Field> getAllDeclaredFields(Class<?> className) {
        List<Field> fields = new ArrayList<Field>();
        Class<?> superClass = className;
        do {
            for(Field field : superClass.getDeclaredFields()) {
            superClass = superClass.getSuperclass();
        while(superClass != null);
        return fields;


  1. Hi Alex,

    I’ve only recently found this post and I find it very useful. However I have a problem. In my business object I have:

    try {
    Myobject p = em.find(Myobject.class, pk);
    } catch (Exception e) {
    if (e.getCause() != null
    && e.getCause() instanceof javax.transaction.RollbackException) {
    javax.transaction.RollbackException re = (javax.transaction.RollbackException) e
    if (re.getCause() != null
    && re.getCause() instanceof javax.persistence.EntityExistsException) {
    javax.persistence.EntityExistsException eee = (javax.persistence.EntityExistsException) re
    if (eee.getCause() != null
    && eee.getCause() instanceof org.hibernate.exception.ConstraintViolationException) {
    org.hibernate.exception.ConstraintViolationException cve = (org.hibernate.exception.ConstraintViolationException) eee
    if (cve.getCause() != null
    && cve.getCause() instanceof java.sql.BatchUpdateException) {
    java.sql.BatchUpdateException bue = (java.sql.BatchUpdateException) cve
    throw new java.sql.BatchUpdateException(bue);

    instead of java.sql.BatchUpdateException I can throw my CustomeException as per your example.

    The thing is that the java.sql.BatchUpdateException can’t be catch in the client method but if I put the same as above directly in the client code then this is working, can you help me with this?

    Thanks a lot,

    PS I know this is old enough post but if you have time please give me a reply.

    Comment by marius — October 12, 2009 @ 4:09 pm

  2. nevermind I’ve found the solution.
    in CMT the em.delete doesn’t commit/rollback the transaction immediately, instead removes the row in the db and the object in java becomes transient. if you call em.flush after em.delete your code will work (catching directly EntityExistsException), but maybe you’re using BMT and not CMT.

    Comment by marius — October 19, 2009 @ 2:46 pm

  3. Your code won’t work in a JEE environment unfortunately. The container commits the transaction AFTER the catch block and the exception is thrown AFTER the catch block so there’s no chance handling constraint violations which are wrapped in PersistenceExceptions actually. Interceptors won’t help either because they are executed before the transaction demarcation as well. The only workaroud I found is to use an interceptor which is annotated as stateless EJB so I can inject the EntityManager into the interceptor and do a flush() manually before my business methods reach the demarcation border. Yes, that is hardcore… :-)

    Comment by Marc Walter — February 8, 2012 @ 9:35 am

    • That’s right, as long as you don’t flush(), JPA won’t send any SQL to the database, the delete won’t happen in the database, and the FK error won’t be triggered. You could just call em.flush() after your em.remove() in application code (make sure you don’t do this thousands of time, flushing is an expensive operation).

      Comment by bosmeeuw — February 8, 2012 @ 9:48 am

  4. Nice stuff.


    Comment by uaihebert — October 15, 2012 @ 2:40 pm

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: