Reference guide:
Model
|
View |
Tabular data |
Object/relational mapping
|
Controllers |
Application |
Customizing
Object relational mapping allows you to declare in which tables and
columns of your database the entity data will be stored.
Object/relational tools allow you to work with objects instead of tables
and columns, and to generate automatically the SQL code to read and update
the database. In this way you do not need direct access to the SQL
database. Of course you have to define precisely how to map your classes
to your tables, and this work is done using JPA mapping annotations.
The OpenXava entities are JPA entities, therefore the object/relational
mapping in OpenXava is done by means of the
Java Persistence API (JPA). This chapter shows the
more basic mapping techniques and some special cases. If you want to learn
more about JPA you can look at
the documentation of Hibernate Annotations (the JPA
implementation used by OpenXava by default), or whatever JPA manual you
want. OpenXava 6.1 and better uses JPA 2.2.
Entity
mapping
The
@Table annotation specifies the primary table
for the annotated entity. Additional tables may be specified using
@SecondaryTable or
@SecondaryTables annotation.
If no
@Table annotation is specified for an entity class, the
default values apply.
Example:
@Entity
@Table(name="CUST", schema="XAVATEST")
public class Customer {
Property
mapping
The
@Column annotation is used to specify a mapped
column for a persistent property or field. If no
@Column
annotation is specified, the default values are applied.
A simple example:
@Column(name="DESC", length=512)
private String description;
An example annotating the getter:
@Column(name="DESC", nullable=false, length=512)
public String getDescription() { return description; }
Other examples:
@Column(name="DESC",
columnDefinition="CLOB NOT NULL",
table="EMP_DETAIL")
@Lob
private String description;
@Column(name="ORDER_COST", updatable=false, precision=12, scale=2)
private BigDecimal cost;
Reference
mapping
The
@JoinColumn annotation is used to specify a
mapped column for a reference.
Example:
@ManyToOne
@JoinColumn(name="CUST_ID")
private Customer customer;
If you need to define a mapping for the composite foreign keys use several
@JoinColumn annotations for the same reference. In this case,
both the
name and the
referencedColumnName elements
must be specified in each such
@JoinColumn annotation.
Example:
@ManyToOne
@JoinColumn(name="INV_YEAR", referencedColumnName="YEAR")
@JoinColumn(name="INV_NUMBER", referencedColumnName="NUMBER")
private Invoice invoice;
For OpenXava versions older than 6.1 (that used the old JPA 2.1) you need
to use
@JoinColumns. This annotation groups
@JoinColumn
annotations for the same reference.
Example:
@ManyToOne
@JoinColumns({ // Only needed until OpenXava 6.0.2/JPA 2.1
@JoinColumn(name="INV_YEAR", referencedColumnName="YEAR"),
@JoinColumn(name="INV_NUMBER", referencedColumnName="NUMBER")
})
private Invoice invoice;
Collection
mapping
When you use
@OneToMany for a collection the mapping
depends of the reference used in the other part of the association, that
is, usually it's not needed to do anything. But if you are using
@ManyToMany, maybe it's useful to declare the
@JoinTable, as following:
@ManyToMany
@JoinTable(name="CUSTOMER_STATE",
joinColumns=@JoinColumn(name="CUSTOMER"),
inverseJoinColumns=@JoinColumn(name="STATE")
)
private Collection<State> states;
If
@JoinTable is missing the default values apply.
When you use
@ElementCollection (new in v5.0) for
a collection you can use
@CollectionTable and
@AttributeOverride, as following:
@ElementCollection
@CollectionTable(name="HOMES") // Uses default join column name
@AttributeOverride(name="street",
column=@Column(name="HOME_STREET"))
@AttributeOverride(name="city",
column=@Column(name="HOME_CITY"))
@AttributeOverride(name="state",
column=@Column(name="HOME_STATE"))
private Collection<Address> vacationHomes;
If you're using an OpenXava older than 6.1 (so with JPA 2.1) you have to
wrapt the
@AttributeOverride annotations with
@AttributeOverrides, thus:
@ElementCollection
@CollectionTable(name="HOMES") // Uses default join column name
@AttributeOverrides({ // Only needed until OpenXava 6.0.2/JPA 2.1
@AttributeOverride(name="street",
column=@Column(name="HOME_STREET")),
@AttributeOverride(name="city",
column=@Column(name="HOME_CITY")),
@AttributeOverride(name="state",
column=@Column(name="HOME_STATE"))
})
private Collection<Address> vacationHomes;
If
@CollectionTable and
@AttributeOverride are
missing the default values apply.
Embedded
reference mapping
An
embedded reference
contains data that in the relational model are stored in the same table as
the main entity. For example, if you have an embeddable
Address
associated to a
Customer, the address data is stored in the same
data table as the customer data. How can you map this case with JPA?
Just use several
@AttributeOverride annotations, in this way:
@Embedded
@AttributeOverride(name="street", column=@Column("ADDR_STREET"))
@AttributeOverride(name="zip", column=@Column("ADDR_ZIP"))
@AttributeOverride(name="city", column=@Column("ADDR_CITY"))
@AttributeOverride(name="country", column=@Column("ADDR_COUNTRY"))
private Address address;
For OpenXava older than 6.1 (with JPA 2.1) use
@AttributeOverrides annotation:
@Embedded
@AttributeOverrides({ // Only until OpenXava 6.0.2/JPA 2.1
@AttributeOverride(name="street", column=@Column("ADDR_STREET")),
@AttributeOverride(name="zip", column=@Column("ADDR_ZIP")),
@AttributeOverride(name="city", column=@Column("ADDR_CITY")),
@AttributeOverride(name="country", column=@Column("ADDR_COUNTRY"))
})
private Address address;
If you do not use
@AttributeOverride default values are
assumed.
Type
conversion
The type conversion between Java and the relational database is a work for
the JPA implementation (OpenXava uses Hibernate by default). Usually, the
default type conversion is good for most cases, but if you work with
legacy database perhaps you need to use the tips here.
Given that OpenXava uses the type conversion facility provided by
Hibernate you can learn more on
Hibernate documentation.
Property
conversion
When the type of a Java property and the type of its corresponding column
in DB do not match you need to write a Hibernate Type in order to do your
custom type conversion.
For example, if you have a property of type
String [], and you
want to store its value concatenating it in a single table column of
VARCHAR type. Then you must declare the conversion for your property in
this way:
@Type(type="org.openxava.test.types.RegionsType")
private String [] regions;
The conversion logic in
RegionsType is:
package org.openxava.test.types;
import java.io.*;
import java.sql.*;
import org.apache.commons.logging.*;
import org.hibernate.*;
import org.hibernate.engine.spi.*; // Since OpenXava 5.3 that uses Hibernate 4.3
import org.hibernate.usertype.*;
import org.openxava.util.*;
/**
*
* @author Javier Paniza
*/
public class RegionsType implements UserType { // 1
public int[] sqlTypes() {
return new int[] { Types.VARCHAR };
}
public Class returnedClass() {
return String[].class;
}
public boolean equals(Object obj1, Object obj2) throws HibernateException {
return Is.equal(obj1, obj2);
}
public int hashCode(Object obj) throws HibernateException {
return obj.hashCode();
}
// SessionImplementer argument since OpenXava 5.3 that uses Hibernate 4.3
// SharedSessionContractImplementor instead of SessionImplementer argument since OpenXava 6.1 that uses Hibernate 5.3
public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor implementor, Object owner) // 2
throws HibernateException, SQLException
{
Object o = resultSet.getObject(names[0]);
if (o == null) return new String[0];
String dbValue = (String) o;
String [] javaValue = new String [dbValue.length()];
for (int i = 0; i < javaValue.length; i++) {
javaValue[i] = String.valueOf(dbValue.charAt(i));
}
return javaValue;
}
// SessionImplementer argument since OpenXava 5.3 that uses Hibernate 4.3
// SharedSessionContractImplementor instead of SessionImplementer argument since OpenXava 6.1 that uses Hibernate 5.3
public void nullSafeSet(PreparedStatement ps, Object value, int index, SharedSessionContractImplementor implementor) // 3
throws HibernateException, SQLException
{
if (value == null) {
ps.setString(index, "");
return;
}
String [] javaValue = (String []) value;
StringBuffer dbValue = new StringBuffer();
for (int i = 0; i < javaValue.length; i++) {
dbValue.append(javaValue[i]);
}
ps.setString(index, dbValue.toString());
}
public Object deepCopy(Object obj) throws HibernateException {
return obj == null?null:((String []) obj).clone();
}
public boolean isMutable() {
return true;
}
public Serializable disassemble(Object obj) throws HibernateException {
return (Serializable) obj;
}
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
}
The type converter has to implement
org.hibernate.usertype.UserType (1). The main
methods are
nullSafeGet (2) for read from database and to
convert to Java, and
nullSafeSet (3) for writing the Java value
into database.
OpenXava has generic Hibernate type converters in the
org.openxava.types package ready to use. One
of them is
EnumLetterType, that allows to map properties
of
enum type. For example, if you have a property like this:
private Distance distance;
public enum Distance { LOCAL, NATIONAL, INTERNATIONAL };
In this Java property 'LOCAL' is 1, 'NATIONAL' is 2 and 'INTERNATIONAL' is
3 when the property is stored in database. But what happens, if in the
database a single letter ('L', 'N' or 'I') is stored? In this case you can
use
EnumLetterType in this way:
@Type(type="org.openxava.types.EnumLetterType",
parameters={
@Parameter(name="letters", value="LNI"),
@Parameter(name="enumType", value="org.openxava.test.model.Delivery$Distance")
}
)
private Distance distance;
public enum Distance { LOCAL, NATIONAL, INTERNATIONAL };
As you put 'LNI' as a value to
letters, the type converter
matches the 'L' to 1, the 'N' to 2 and the 'I' to 3. You also see how type
converters are configurable using its properties and this makes the
converters more reusable.
Multiple
column conversion
With
CompositeUserType you can map several table
columns to a single Java property. This is useful if you have properties
of custom class that have itself several attributes to store. Also it is
used when you have to deal with legacy database schemes.
A typical example is the generic converter
Date3Type, that allows to store in the
database 3 columns and in Java a single property of type
java.util.Date.
@Type(type="org.openxava.types.Date3Type")
@Columns(columns = {
@Column(name="YEARDELIVERY"),
@Column(name="MONTHDELIVERY"),
@Column(name="DAYDELIVERY")
})
private java.util.Date deliveryDate;
DAYDELIVERY, MONTHDELIVERY and YEARDELIVERY are 3 columns in database that
store the delivery date. And here
Date3Type:
package org.openxava.types;
import java.io.*;
import java.sql.*;
import org.hibernate.*;
import org.hibernate.engine.*; // Until OpenXava 5.2.x
import org.hibernate.engine.spi.*; // Since OpenXava 5.3 that uses Hibernate 4.3
import org.hibernate.type.*;
import org.hibernate.usertype.*;
import org.openxava.util.*;
/**
* In java a <tt>java.util.Date</tt> and in database 3 columns of
* integer type. <p>
*
* @author Javier Paniza
*/
public class Date3Type implements CompositeUserType { // 1
public String[] getPropertyNames() {
return new String[] { "year", "month", "day" };
}
public Type[] getPropertyTypes() {
// return new Type[] { Hibernate.INTEGER, Hibernate.INTEGER, Hibernate.INTEGER }; // Before OpenXava 5.3/Hibernate 4.3
return new Type[] { IntegerType.INSTANCE, IntegerType.INSTANCE, IntegerType.INSTANCE }; // Since OpenXava 5.3/Hibernate 4.3
}
public Object getPropertyValue(Object component, int property) throws HibernateException { // 2
java.util.Date date = (java.util.Date) component;
switch (property) {
case 0:
return Dates.getYear(date);
case 1:
return Dates.getMonth(date);
case 2:
return Dates.getDay(date);
}
throw new HibernateException(XavaResources.getString("date3_type_only_3_properties"));
}
public void setPropertyValue(Object component, int property, Object value)
throws HibernateException // 3
{
java.util.Date date = (java.util.Date) component;
int intValue = value == null?0:((Number) value).intValue();
switch (property) {
case 0:
Dates.setYear(date, intValue);
case 1:
Dates.setMonth(date, intValue);
case 2:
Dates.setDay(date, intValue);
}
throw new HibernateException(XavaResources.getString("date3_type_only_3_properties"));
}
public Class returnedClass() {
return java.util.Date.class;
}
public boolean equals(Object x, Object y) throws HibernateException {
if (x==y) return true;
if (x==null || y==null) return false;
return !Dates.isDifferentDay((java.util.Date) x, (java.util.Date) y);
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
// SharedSessionContractImplementor instead of SessionImplementer argument since OpenXava 6.1 that uses Hibernate 5.3
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException // 4
{
/* Before OpenXava 5.3/Hibernate 4.3
Number year = (Number) Hibernate.INTEGER.nullSafeGet( rs, names[0] );
Number month = (Number) Hibernate.INTEGER.nullSafeGet( rs, names[1] );
Number day = (Number) Hibernate.INTEGER.nullSafeGet( rs, names[2] );
*/
// Since OpenXava 5.3/Hibernate 4.3
Number year = (Number) IntegerType.INSTANCE.nullSafeGet( rs, names[0], session, owner);
Number month = (Number) IntegerType.INSTANCE.nullSafeGet( rs, names[1], session, owner );
Number day = (Number) IntegerType.INSTANCE.nullSafeGet( rs, names[2], session, owner );
int iyear = year == null?0:year.intValue();
int imonth = month == null?0:month.intValue();
int iday = day == null?0:day.intValue();
return Dates.create(iday, imonth, iyear);
}
// SharedSessionContractImplementor instead of SessionImplementer argument since OpenXava 6.1 that uses Hibernate 5.3
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException // 5
{
java.util.Date d = (java.util.Date) value;
/* Before OpenXava 5.3/Hibernate 4.3
Hibernate.INTEGER.nullSafeSet(st, Dates.getYear(d), index);
Hibernate.INTEGER.nullSafeSet(st, Dates.getMonth(d), index + 1);
Hibernate.INTEGER.nullSafeSet(st, Dates.getDay(d), index + 2);
*/
// Since OpenXava 5.3/Hibernate 4.3
IntegerType.INSTANCE.nullSafeSet(st, Dates.getYear(d), index, session);
IntegerType.INSTANCE.nullSafeSet(st, Dates.getMonth(d), index + 1, session);
IntegerType.INSTANCE.nullSafeSet(st, Dates.getDay(d), index + 2, session);
}
public Object deepCopy(Object value) throws HibernateException {
java.util.Date d = (java.util.Date) value;
if (value == null) return null;
return (java.util.Date) d.clone();
}
public boolean isMutable() {
return true;
}
// SharedSessionContractImplementor instead of SessionImplementer argument since OpenXava 6.1 that uses Hibernate 5.3
public Serializable disassemble(Object value, SharedSessionContractImplementor session)
throws HibernateException
{
return (Serializable) deepCopy(value);
}
// SharedSessionContractImplementor instead of SessionImplementer argument since OpenXava 6.1 that uses Hibernate 5.3
public Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner)
throws HibernateException
{
return deepCopy(cached);
}
// SharedSessionContractImplementor instead of SharedSessionContractImplementor argument since OpenXava 6.1 that uses Hibernate 5.3
public Object replace(Object original, Object target, SessionImplementor session, Object owner)
throws HibernateException
{
return deepCopy(original);
}
}
As you see the type converter implements
CompositeUserType (1). The key methods are
getPropertyValue
(2) and
setPropertyValue (3) to get and to set values in the
properties of the object of the composite type, and
nullSafeGet
(4) and
nullSafeSet (5) for reading and storing this object from
and to database.
Reference
conversion
Reference conversion is not supported directly by Hibernate. But in some
very rare circumstances maybe you need to do conversion in the reference.
In this section we explain how to do it.
For example, you may have a reference to driver licence using two columns,
DRIVINGLICENCE_LEVEL and DRIVINGLICENCE_TYPE, and the DRIVINGLICENCE_TYPE
column does not admit null, but it's possible that the object can have no
reference to driving lincence in which case the column DRIVINGLICENCE_TYPE
hold an empty string. This is not a normal case if you design the database
using foreign keys, but if the database was designed by a RPG programmer,
for example, this was done in this way, because RPG programmer are not
used to cope with nulls.
That is, you need a conversion for DRIVINGLICENCE_TYPE, for transform null
to empty string. This can be achieve with a code like this:
// We apply conversion (null into an empty String) to DRIVINGLICENCE_TYPE column
// In order to do it, we create drivingLicence_level and drivingLicence_type
// We make JoinColumns not insertable nor updatable, we modify the get/setDrivingLincence methods
// and we create a drivingLicenceConversion() method.
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumns({ // 1
@JoinColumn(name="DRIVINGLICENCE_LEVEL", referencedColumnName="LEVEL",
insertable=false, updatable=false),
@JoinColumn(name="DRIVINGLICENCE_TYPE", referencedColumnName="TYPE",
insertable=false, updatable=false)
})
private DrivingLicence drivingLicence;
private Integer drivingLicence_level; // 2
private String drivingLicence_type; // 2
public DrivingLicence getDrivingLicence() { // 3
// In this way because the column for type of driving lincence does not admit null
try {
if (drivingLicence != null) drivingLicence.toString(); // to force load
return drivingLicence;
}
catch (EntityNotFoundException ex) {
return null;
}
}
public void setDrivingLicence(DrivingLicence licence) { // 4
// In this way because the column for type of driving lincence does not admit null
this.drivingLicence = licence;
this.drivingLicence_level = licence==null?null:licence.getLevel();
this.drivingLicence_type = licence==null?null:licence.getType();
}
@PrePersist @PreUpdate
private void drivingLicenceConversion() { // 5
if (this.drivingLicence_type == null) this.drivingLicence_type = "";
}
First, you have to use
@JoinColumns with
insertable=false
and
updatable=false on all
@JoinColumn (1), in this way the reference is
readed from database, but it is not write. Also define plain properties
for storing the foreign key of the reference (2).
Now you must write a getter,
getDrivingLicence() (3), for
returning null when the reference is not found; and a setter,
setDrivingLicence()
(4), for assigning the key of the reference to the correspoding plain
properties.
Finally, you have to write a
callback
method,
drivingLincenceConversion() (5), to do the
conversion work. This method will be automatically executed on create and
update.
This example shows how it's possible to wrap legacy databases simply using
a little of programming and some basic resources from JPA.
Unique
Constraints
Since v4.9 openxava lets you to customize messages of declared constraints
in the element
uniqueConstraints of
@Table and
@SecondaryTable, as well as
@Column(unique
= true). To do this, keep in mind some preliminary considerations
regarding Hibernate (The JPA implementation used by default by openxava).
Example:
package org.openxava.test.model;
@Entity
@SecondaryTable(
name="APPLICATIONUSER_INFO",
uniqueConstraints={
@UniqueConstraint(name="not_repeat_user_info", columnNames={"name", "birthdate", "sex"})
}
)
public class ApplicationUser extends Identifiable {
@Required
@Column(length=8, unique=true) //not_repeat_nic
private String nic;
@Column(length=40, table="APPLICATIONUSER_INFO")
private String name;
@Column(length=40, table="APPLICATIONUSER_INFO")
private Date birthdate;
@Column(table="APPLICATIONUSER_INFO")
@Enumerated(EnumType.STRING)
private Sex sex;
public enum Sex { MALE, FEMALE }
...
}
- By building our application (running build.xml), Hibernate
will use the declared dialect in the persistence.xml
of your project, to generate a DDL to map the structure of your classes
to tables in the database. If, for example, you are working with a MySQL
database, display:
create table APPLICATIONUSER_INFO (
birthdate datetime,
name varchar(40),
sex varchar(255),
id varchar(32) not null,
primary key (id), unique (name, birthdate, sex)
)
create table ApplicationUser (
id varchar(32) not null,
nic varchar(8) unique,
primary key (id)
)
alter table APPLICATIONUSER_INFO
add index FK375C9572BA846971 (id),
add constraint FK375C9572BA846971 foreign key (id) references ApplicationUser (id)
As can be seen Hibernate has mapped the structure of our
ApplicationUser
class has even created the constraint
unique(name, birthdate,
sex) and
nic varchar (8) unique
but did not assign declared name
("no_repeat_user_info") in
@UniqueConstraint
nor there is an element in
@Column that allows
us name the constraint
unique=true, letting the
database engine assigns default names.
- When a violation of any of the above constraints occur, Hibernate
will manage the error thrown by the database engine, creating a org.hibernate.exception.ConstraintViolationException
or in some cases a org.hibernate.exception.GenericJDBCException
-as is the HSQL. ConstraintViolationException
has a constraintName property that is
assigned by Hibernate after extracting the constraint name that has
the database.
- The dialect defined in your project is responsible for extract the
constraint name to be assigned to contraintName.
It does this through the extractConstraintName(SQLException
sqle) method of ViolatedConstraintNameExtracter
interface. But it happens that the dialects provided by Hibernate not
always carried out properly constraintName
extraction. For our example, MySQL5 dialect of Hibernate
-Hibernate3.6.10 using openxava- nor implements ViolatedConstraintNameExtracter
interface -Hibernate4 already does it adequately.
Therefore, if We want to generate a custom message to the above
constraints, We do:
- To maintain consistency between the name of the @UniqueConstraint
and the name of the constraint in the database, wich Hibernate will
manage through the constraintName of ConstraintViolationException,
mapping manually the name of the constraint in the database:
create table APPLICATIONUSER_INFO (
birthdate datetime,
name varchar(40),
sex varchar(255),
id varchar(32) not null,
primary key (id),
unique key `not_repeat_user_info` (name, birthdate, sex)
)
- Same for the constraint of @Column:
create table ApplicationUser (
id varchar(32) not null,
nic varchar(8),
primary key (id),
unique key `not_repeat_nic` (nic)
)
- If the dialect is not adequate we redefine it:
package dialect;
import java.sql.*;
import org.hibernate.dialect.*;
import org.hibernate.exception.*;
public class XMySQL5Dialect extends MySQL5Dialect {
public XMySQL5Dialect(){}
private static ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
public String extractConstraintName(SQLException sqle) {
try {
int sqlState = Integer.valueOf( JDBCExceptionHelper.extractSqlState(sqle)).intValue();
switch (sqlState) {
case 23000: return extractUsingTemplate("for key '","'", sqle.getMessage());
default: return null;
}
} catch (NumberFormatException nfe) {
return null;
}
}
};
@Override
public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
return EXTRACTER;
}
}
- We assign the new dialect in persistence.xml
of our project:
...
<property name="hibernate.dialect"value="dialect.XMySQL5Dialect"/>
...
- Finally, We declare the names assigned to the constraints as message
identifiers in the i18n file of our application.
Other
dialects
import java.sql.*;
import org.hibernate.dialect.*;
import org.hibernate.exception.*;
public class XPostgreSQLDialect extends PostgreSQLDialect {
public XPostgreSQLDialect(){}
private static ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
public String extractConstraintName(SQLException sqle) {
try {
int sqlState = Integer.valueOf( JDBCExceptionHelper.extractSqlState(sqle)).intValue();
switch (sqlState) {
// CHECK VIOLATION
case 23514: return extractUsingTemplate("violates check constraint \"","\"", sqle.getMessage());
// UNIQUE VIOLATION
case 23505:
if (sqle.getMessage().indexOf("violates unique constraint \"") > -1)
return extractUsingTemplate("violates unique constraint \"","\"", sqle.getMessage());
else if (sqle.getNextException() != null )
return extractConstraintName(sqle.getNextException());
else
return "UNIQUE_CONSTRAINT_VIOLATION_UNKNOWN";
// FOREIGN KEY VIOLATION
case 23503: return extractUsingTemplate("violates foreign key constraint \"","\"", sqle.getMessage());
// NOT NULL VIOLATION
case 23502: return extractUsingTemplate("null value in column \"","\" violates not-null constraint", sqle.getMessage());
// RESTRICT VIOLATION
case 23001: return null;
// ALL OTHER
default: return null;
}
} catch (NumberFormatException nfe) {
return null;
}
}
};
@Override
public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
return EXTRACTER;
}
}
import java.sql.*;
import org.hibernate.dialect.*;
import org.hibernate.exception.*;
public class XSQLServerDialect extends SQLServer2008Dialect {
public XSQLServerDialect() {}
private static ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
public String extractConstraintName(SQLException sqle) {
try {
int sqlState = Integer.valueOf(JDBCExceptionHelper.extractSqlState(sqle)).intValue();
switch (sqlState) {
case 23000:
return extractUsingTemplate("UNIQUE KEY '", "'", sqle.getMessage());
default:
return null;
}
} catch (NumberFormatException nfe) {
return null;
}
}
};
@Override
public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
return EXTRACTER;
}
}