I'm using Hibernate 5.2 with Postgre 9.5.
My DB schema has several constraints. Is there any "easy" possibility to expose constraint name to end user? Imagine I have unique constraint
ALTER TABLE JobTable
ADD CONSTRAINT JobTableJobStatusEnum
CHECK (jobStatus IN ('JobSubmitted', 'JobRunning', 'JobFailed', 'JobKilled', 'JobSucceeded'));
I want to re-throw something like
ConstraintViolationException("Can't execute operation. Your query breaks JobTableJobStatusEnum")
Yeah, It won't make any sense to business users, but other developers would understand the problem. I can grab constraint def from DB and put it into exception message also.
Basically you have constraints information in exception - stacktrace.
If you need to wrap original exception and add something custom (it's not so custom it's just change exception message), you can catch ConstraintViolationException (or exception and check that it's caused by ConstraintViolationException with apache commons-lang ExceptionUtils util)
catch(ConstraintViolationException e){
Strign message = e.getCause().getMessage();
.....
work with message
String detailMessage = String.format("Can't execute operation. Your query breaks %" , message);
throw new ConstraintViolationException(detailMessage , e);
}
For mysql (for Postgre should be something similar) it looks like
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Duplicate entry 'testemail#gmail.com' for key
'UK_n7ihswpy07ci568w34q0oi8he'
If you know format message for ConstraintViolation in your db , you can get constraint name from exception message method.
Related
I have a typical scenario where users enter data that is inserted into a SQL database using Entity Framework 6.0. However, some rows that are part of the entity need to be unique (already enforced with unique key constraints in the database).
To avoid possible concurrency or performance issues I favour these checks to be left done by SQL Server.
When attempting to save a new entity that holds a duplicate row, a DbUpdateException is thrown by Entity Framework. The inner exception is a SqlException with its Number equal to 2627, and a message that reads:
"Violation of UNIQUE KEY constraint 'UK_MyTable_MyRule'. Cannot insert duplicate key in object 'dbo.MyTable'".
Considering that there are several tables involved, which may each have their own unique constraints defined, is there no better way to conclude a friendlier message to the user that reads:
"A MyEntity with the name 'MyEntity1' already exists."
...without having to infer this through the Number and Message properties from the SqlException?
For example:
try
{
...
context.SaveChanges();
}
catch (DbUpdateException exception)
{
var sqlException = exception.InnerException as SqlException;
bool isDuplicateInMyTable3 =
sqlException != null &&
sqlException.Number = 2627/*Unique Constraint Violation*/ &&
sqlException.Message.Contains("'UK_MyTable3_");
if (isDuplicateInMyTable3)
{
return "A MyTable3 with " + ... + " already exists.";
}
throw exception;
}
Is there a "cleaner" way to achieve the same that does not involve looking through the error message string?
You may like to enjoy the AddOrUpdate method.
Research it first. I have noted experts warning of over zealous use.
Context.Set<TPoco>().AddOrUpdate(poco);
can still throw other EF\DB exceptions.
But Duplicate primary key should not be one of them.
Other constraint issues are as before.
Since EF doesn't support unique key contraints, it seems that we need to catch exception during the Save method, and display error message to user.
The problems with this approach are:
how do we know which record threw an exception
how do we know what kind of problem threw an exception (ex I could have two unique constraints on same record, so I need to tell the user which one is broken)
DBMS is SqlServer 2008.
How to resolve these problems?
If you allow that a user can enter values that must be unique in the database you should validate this input before you save the changes:
if (context.Customers.Any(c => c.SomeUniqueProperty == userInput))
// return to user with a message to change the input value
else
context.SaveChanges();
This isn't only the case for values with unique constraints in the database but also for entering foreign key values that must refer to existing target records or primary key values if the primary key isn't autogenerated in the database. EF doesn't help you in the latter situation either because a context doesn't know the content of the whole database table but only about the entities that are currently attached to the context. It is true that EF will forbid to attach two objects with the same primary key but allows two objects with the same unique key constraint. But this doesn't guard you completely against primary key constraint violations when you save changes to the database.
In the unlikely case that between the Any check and SaveChanges another user has entered a record with the same value, I would consider the occuring exception as not possible to handle and just tell the user "An expected error occurred, please try again...". If the user tries again the Any check happens again and he will get the more useful message to change the input value from the code above.
The exception returned for such unique key constraint or primary key constraint violations is a general DbUpdateException and one of the inner exceptions will be a SqlException that contains as one of its properties a SQL Server error code. Any other details can be found only in the exception message "Violation of UNIQUE KEY constraint IX_SomeUniqueProperty_Index..." or similar. If you expect that the user can understand and react accordingly to this information you could display it. Otherwise you could log this message for an administrator or developer to check for possible bugs or other problems.
We use EclipseLink for persistence, and have configured EclipseLink to automatically create the database tables etc., by setting the property eclipselink.ddl-generation to drop-and-create-tables.
This works fine, however EclipseLink (and thus our app) will merrily continue during unit tests, and on actual web app startup even if some of the DDL statements failed.
I noticed this when I incorrectly used the #Index annotation, and wondered why the index was not created, until I noticed in the logs:
org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLException:
Column "MY_INDEX_FLD" not found; SQL statement:
CREATE INDEX X_INDEX ON X (MY_INDEX_FLD)
I really want to know if this happens. Is there some way to tell EclipseLink to make it a fatal error if some DDL statements fails?
I'd like to at least have our (JUnit) integration tests fail in this case.
Bonus points for some way to be able to ignore if the error is simply that the tables are already there (in the case of testing against an existing database).
Apparently, EclipseLink cannot do this.
I had a look at the method in EclipseLink that creates tables:
org.eclipse.persistence.tools.schemaframework.TableCreator.createTables()
(search for "createTables" on the page).
I contains the lines:
try {
schemaManager.createObject(table);
session.getSessionLog().log(SessionLog.FINEST,
"default_tables_created", table.getFullName());
} catch (DatabaseException ex) {
session.getSessionLog().log(SessionLog.FINEST,
"default_tables_already_existed", table.getFullName());
if (!shouldIgnoreDatabaseException()) {
throw ex;
}
}
So apparently EclipseLink assumes that errors during table (and index) creation are always caused by the table already existing :-(.
I filed an EclipseLink bug for this: Bug 356068.
I currently have a system which required regular data imports. The db is Postgresql.
What I want to do is validate if a foreign key does not exist on a dataload.
For example
Customer belongs to Title and the 'titles' table contains
( id : name )
1 : Mr
2 : Mrs
3 : Miss
If during the dataload I pass in say 22 as a title_id, postgres throws a foreign key constraint violation which throws a php warning. Does anyone know of a way to catch this rather me having to write custom validation rules?
Thanks
Leo
If using PDO, you can set the error mode to throw exceptions, like such:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
You can then catch the exception and do whatever you please with it.
Can somebody tell me the intrinsic reasons why in the JPA 1.0 EntityManager when retrieving an Object via find, you have to deal with null if not found, but when using the Query interface via createQuery getResultList throws a NoResultException when not found.
Maybe i am missing something but I feel its very inconsistent for a Language, and actually I had to do a lot of redesing because of changing from a simple finder to a more fine grained query using the query interface.
Thanks guys.
Queries can be used to retrieve almost anything including the value of a single column in a single row.
If getSingleResult() would return null, you could not tell whether the query did not match any row or whether the query matched a row but the selected column contains null as its value.
When you do a find, jpa will use the primary key to locate the entity object, often using second level cache and it is typically much faster than createQuery and getSingleResult.
You either get null or the Object back from find. When you do a createQuery and instance of Query object is created. If you do a getResultList it will not throw a NoResultException, only if you do a getSingleResult will it throw that exception. If you do a getResultList and none is found, then null will be returned.
Also, NoResultException will mark the transaction rolledback in weblogic 10.3.2.
See this article: NoResultException marks transaction rollback
I think it eliminates this null check :
Object o = q.getSingleResult();
if (o != null)
return (MyObj) o;
return o;
By introducing a RuntimeException (NoResultException) , programmers can safely cast q.getSingleResult() to MyObj , and leave the exception to the caller.
As to q.getResultList() , it will always return a list , null-check is not necessary.
But I still feel this non-intuitive.