Yes, I found a solution, but the annoying thing is that is doesn't work the same all the time. I have both seam and struts apps that needed this and you have to tweak the while loop in the getViolatedConstraintName(). Also I am doing this for postgres. Your milage will vary for other db's I am sure
Code:
import org.hibernate.exception.ConstraintViolationException;
import org.postgresql.util.PSQLException;
/**
 *
 * @author turekg
 */
public class ExceptionUtils {
  
  /**
   * Gets the name of the violated constraint. Note call hierarchy is different than Struts' !
   *
   * @param ex RuntimeException
   * @return constraint name
   *
   */
  public static String getViolatedConstraintName(Throwable t) {
    Throwable cause = t;
    while (cause != null) {
      if (cause instanceof PSQLException) {
       break;
      }
      cause = cause.getCause();
    }
    //We are now dealing with a ConstraintViolationException
    PSQLException psqlException =  (PSQLException) cause;
    
    int sqlState = Integer.valueOf(psqlException.getSQLState()).intValue();
    switch (sqlState) {
      // CHECK VIOLATION
      case 23514: return extractUsingTemplate("violates check constraint \"","\"", psqlException.getMessage());
      // UNIQUE VIOLATION
      case 23505: return extractUsingTemplate("violates unique constraint \"","\"", psqlException.getMessage());
      // FOREIGN KEY VIOLATION
      case 23503: return extractUsingTemplate("violates foreign key constraint \"","\"", psqlException.getMessage());
      // NOT NULL VIOLATION
      case 23502: return extractUsingTemplate("null value in column \"","\" violates not-null constraint", psqlException.getMessage());
      // TODO: RESTRICT VIOLATION
      case 23001: return null;
      // ALL OTHER
      default: return null;
    }
  }
  
  public static boolean isViolatedConstraint(Throwable t) {
    Throwable cause = t;
    while (cause != null) {
      if (cause instanceof ConstraintViolationException) {
        return true;
      }
      cause = cause.getCause();
    }
    
    return false;
  }
  
      /**
   * Extracts the constraint name based on a template (i.e.,
   * <i>templateStart</i><b>constraintName</b><i>templateEnd</i>).
   * 
   * @param templateStart
   *          The pattern denoting the start of the constraint name within the
   *          message.
   * @param templateEnd
   *          The pattern denoting the end of the constraint name within the
   *          message.
   * @param message
   *          The templated error message containing the constraint name.
   * @return The found constraint name, or null.
   */
  private static String extractUsingTemplate(String templateStart, String templateEnd, String message) {
    int templateStartPosition = message.indexOf(templateStart);
    if (templateStartPosition < 0) {
      return null;
    }
    int start = templateStartPosition + templateStart.length();
    int end = message.indexOf(templateEnd, start);
    if (end < 0) {
      end = message.length();
    }
    return message.substring(start, end);
  } 
}