Hibernate QueryException - java

Hello I am trying to reference in a Criteria a property of a composite key which is defined as and #Embeddable on an Entity
#Entity
#Table(name = "B_J_P")
public class BJP implements java.io.Serializable {
private BJPId id;
private BJI bJI;
public BJP() {
}
public BJP(BJPId id, BJI bJI) {
this.id = id;
this.bJI = bJI;
}
#EmbeddedId
#AttributeOverrides( {
#AttributeOverride(name = "jIId", column = #Column(name = "J_I_ID", nullable = false)),
#AttributeOverride(name = "kN", column = #Column(name = "K_N", nullable = false, length = 100)),
public BJPId getId() {
return this.id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "J_I_ID", nullable = false, insertable = false, updatable = false)
public BJI getBJI() {
return this.bJI;
}
}
I need to reach the kName from the following:
#Embeddable
public class BJPId implements java.io.Serializable {
private long jIId;
private String kName;
public BJPId() {
}
public BJPId(long jIId, String kN) {
this.jIId = jIId;
this.kN = kN;
}
#Column(name = "J_I_ID", nullable = false)
public long getJIId() {
return this.jIId;
}
#Column(name = "K_NAME", nullable = false, length = 100)
public String getKName() {
return this.kName;
}
}
But when I am trying to reach it from the base class where BJP is a property with the following Criteria
DetachedCriteria timestampFilter = DetachedCriteria.forClass(BJP.class)
.createAlias("id","alias")
.add(Restrictions.eq("alias.kName","DataSetName"))
.setProjection(Projections.property("kName"));
I get the following error:
org.hibernate.QueryException: Criteria objects cannot be created directly on components. Create a criteria on owning entity and use a dotted property to access component property: id
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getPathInfo
How should I formulate the criteria query in order to reach the kName property to apply filtering based on it in a dynamic sql context ?
If I have not provided enough relevant information, please ask what have I forgotten to provide full context.
EDIT: Upon Genzetto advice I have managed to reach the elements(at least it is not giving errors now) but returns no results once I do this:
DetachedCriteria timestampFilter = DetachedCriteria.forClass(BJP.class)
.add(Restrictions.eq("id.kName","DataSetName"))
.setProjection(Projections.property("id.kName"));
Session currentSession = sessionFactory.getCurrentSession();
Criteria query = currentSession.createCriteria(BJI.class)
.add(Subqueries.propertyEq("bJP",timestampFilter))
as upon looking at the SQL it is of the format
... where this_.J_INST_ID = (select this_.K_NAME as y0_ from .B_J_P this_ where this_.K_NAME=?)
it is trying to add the subquery to the ID of the root object although I want it part of bJP. How can I add it to proper location ?

You don't need to use an alias to do this. You can access directly to the composite key attributes:
DetachedCriteria timestampFilter = DetachedCriteria.forClass(BJP.class)
.add(Restrictions.eq("id.kName","DataSetName"))
.setProjection(Projections.property("id.kName"));

Related

java jpa one to one relation is always null for one side?

I have two entity booking and travelAgentBooking, booking could exist by itself while travelAgentBooing must have one booking of it.
TABookingEntity is below
#Entity
#ApplicationScoped
#Table(name = "TABooking")
#NamedQuery(name = "TABooking.findAll", query = "SELECT t FROM TABookingEntity t ORDER BY t.id ASC")
public class TABookingEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TABookingId_seq")
#SequenceGenerator(name = "TABookingId_seq", initialValue = 1, allocationSize = 1)
private Long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "booking_id", nullable = false)
private BookingEntity flightbooking;
// belong to upstream booking so we just store id here
private Long taxibookingid;
private Long hotelbookingid;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public BookingEntity getFlightbooking() {
return flightbooking;
}
public void setFlightbooking(BookingEntity flightbooking) {
this.flightbooking = flightbooking;
if (flightbooking != null) {
flightbooking.setTravelAgentBooking(this);
}
}
public Long getTaxibookingId() {
return taxibookingid;
}
public void setTaxibookingId(Long taxibookingid) {
this.taxibookingid = taxibookingid;
}
public Long getHotelbookingId() {
return hotelbookingid;
}
public void setHotelbookingId(Long hotelbookingid) {
this.hotelbookingid = hotelbookingid;
}
BookingEntity is below
#Entity
#ApplicationScoped
#Table(name = "booking")
#NamedQueries({ #NamedQuery(name = "Booking.findAll", query = "SELECT b FROM BookingEntity b ORDER BY b.d ASC"),
#NamedQuery(name = "Booking.findByFlight", query = "SELECT b FROM BookingEntity b WHERE b.flight = :flight"),
#NamedQuery(name = "Booking.findByDate", query = "SELECT b FROM BookingEntity b WHERE b.d = :d") })
public class BookingEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bookingId_seq")
#SequenceGenerator(name = "bookingId_seq", initialValue = 1, allocationSize = 1)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "customer_id", nullable = false)
private CustomerEntity customer;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "flight_id", nullable = false)
private FlightEntity flight;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "travelAgentBooking_id", nullable = true)
private TABookingEntity travelAgentBooking;
#NotNull
#Column(name = "date")
private Date d;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public CustomerEntity getCustomer() {
return customer;
}
public void setCustomer(CustomerEntity customer) {
this.customer = customer;
if(customer != null)
customer.addBooking(this);
}
public FlightEntity getFlight() {
return flight;
}
public void setFlight(FlightEntity flight) {
this.flight = flight;
}
public Date getDate() {
return new Date(d.getTime());
}
public void setDate(Date d) {
this.d = d;
}
public TABookingEntity getTravelAgentBooking() {
return travelAgentBooking;
}
public void setTravelAgentBooking(TABookingEntity travelAgentBooking) {
this.travelAgentBooking = travelAgentBooking;
}
here is the code I creating booking firstly, and then set it to tabooking.
then I'm trying to update the booking since when it is created, there is no travelAngentBooking for it to associate.
Booking booking = flightService.createBooking(tabooking.getFlightbooking());
tabooking.setFlightbooking(booking);
,,,,,,,,,,,
,,,,,,,,,,,
tabookingService.create(tabooking);
flightService.updateBooking(tabooking.getFlightbooking().getId(), tabooking.getFlightbooking());
After running it the table of travelAgentBooking is perfect.
But booking table column referred to travelAgentBooking is always null for any booking object.
UPDATE:
#PUT
#Path("/{id:[0-9]+}")
#Operation(description = "Update a Booking in the database")
#APIResponses(value = { #APIResponse(responseCode = "200", description = "Booking updated successfully"),
#APIResponse(responseCode = "400", description = "Invalid Booking supplied in request body"),
#APIResponse(responseCode = "404", description = "Booking with id not found"),
#APIResponse(responseCode = "409", description = "Booking details supplied in request body conflict with another existing Booking"),
#APIResponse(responseCode = "500", description = "An unexpected error occurred whilst processing the request") })
#Transactional
public Response updateBooking(
#Parameter(description = "Id of Booking to be updated", required = true) #Schema(minimum = "0") #PathParam("id") Integer id,
#Parameter(description = "JSON representation of Booking object to be updated in the database", required = true) Booking booking) {
Customer customer = customerService.findById(booking.getCustomer().getId())
.orElseThrow(() -> new RestServiceException("We can't found customer", Response.Status.BAD_REQUEST));
if (!customer.equals(booking.getCustomer()))
throw new RestServiceException("use custoemr's own API for it update", Response.Status.BAD_REQUEST);
Flight flight = flightService.findById(booking.getFlight().getId())
.orElseThrow(() -> new RestServiceException("We can't found flight", Response.Status.BAD_REQUEST));
if (!flight.equals(booking.getFlight()))
throw new RestServiceException("use custoemr's own API for it update", Response.Status.BAD_REQUEST);
try {
bookingService.validateBooking(booking);
} catch (ConstraintViolationException ce) {
// Handle bean validation issues
Map<String, String> responseObj = new HashMap<>();
for (ConstraintViolation<?> violation : ce.getConstraintViolations()) {
responseObj.put(violation.getPropertyPath().toString(), violation.getMessage());
}
throw new RestServiceException("Bad Request", responseObj, Response.Status.BAD_REQUEST, ce);
} catch (UniqueFlightWithDateException e) {
// we are updating an existence flight, so ignore this as expected
}
try {
bookingService.update(id);
} catch (ServiceException e) {
Map<String, String> responseObj = new HashMap<>();
responseObj.put("id", "please ensure the id is associated with this number");
throw new RestServiceException("Bad Request", responseObj, Response.Status.NOT_FOUND, e);
}
bookingService.update(id);
return Response.ok(booking).build();
}
BookingEntity update(BookingEntity booking) {
log.info("BookingRepository.update() - Updating " + booking.getId());
em.merge(booking);
return booking;
}
From the original posted code, the problem is that you have two very independent unidirectional relationships and only setting one of them. Since they are independent, the other remains null and can not be anything other than null until it is set.
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "booking_id", nullable = false)
private BookingEntity flightbooking;
The join column sets a foreign key in the "TABooking" table to point at the bookingEntity. It requires this relationship reference be set to populate that foreign key value. Same thing with:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "travelAgentBooking_id", nullable = true)
private TABookingEntity travelAgentBooking
It creates its own travelAgentBooking_id foreign key column in the "booking" table that will remain null until you update a booking instance and set this reference. If you only set one side, the other will always remain null in the database.
But there are two problems with the model and your expectations. First, from the comments, you didn't intend this to be two separate relationships and instead expect it to be a single bidirectional relationship. For that, you need a single foreign key, and to pick a side that 'owns' it. The side that owns it controls it:
#OneToOne(mappedBy "flightbooking", fetch = FetchType.LAZY)
private TABookingEntity travelAgentBooking
Using mappedBy tells JPA that the other side owns the relationship. The foreign key column then is only set when you set the TABookingEntity.flightbooking reference and save/merge the TABookingEntity instance.
Second is you are using JSON and so Json serialization and assuming it abides by your object model and the JPA mappings. It does not. JPA annotations are for your persistence provider to tell it how to serialize/deserialize your model into the database but mean nothing for JSON serialization (or xml or any other REST formats). You need to tell your JSON tool how to handle your relationships, and that completely depends on how you are going to be expecting and sending the JSON. There are many tutorials and different strategies to deal with this (see this link for a good primer), but easiest is usually just to pick parts of the graph and exclude them with #JsonIgnore:
#OneToOne(mappedBy "flightbooking", fetch = FetchType.LAZY)
#JsonIgnore
private TABookingEntity travelAgentBooking
This will mean that any JSON you receive representing a booking will have a null travelAgentBooking. So if you need to see or set this relationship, your api would have to send/receive TABookingEntity which would still have the flightbooking reference serialized. I picked this way because flightbooking owns the relationship, so it matches JPA, but it doesn't need to. You can and should figure out what works for your client application and it may be different from the JPA mappings. I would expect that bookings always need to know the TABookingEntity and you'll want that sent to the client, so you might put the #JsonIgnore annotation on the other side. If you do, you'll just have to be sure that when you want to change or add TABookingEntity, that you fix the TABookingEntity.flightbooking reference appropriately so that you don't null out the foreign key.

Hibernate Envers is not able to "locate getter method for property" for Embeddable table

I have a class with #Audit annotation as below
#Entity
#Audited(withModifiedFlag=true)
#Table(name = "PERIODICITY")
public class Periodicity implements java.io.Serializable {
private PeriodicityId id;
private String frequency;
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "instrumentId", column = #Column(name = "INSTRUMENT_ID", nullable = false, precision = 22, scale = 0)),
#AttributeOverride(name = "legType", column = #Column(name = "LEG_TYPE", nullable = false, precision = 38, scale = 0))})
public PeriodicityId getId() {
return this.id;
}
public void setId(PeriodicityId id) {
this.id = id;
}
#Column(name = "FREQUENCY", nullable = false, length = 20)
public String getFrequency() {
return this.frequency;
}
}
And the Embedded class is as follows
#Embeddable
public class PeriodicityId implements java.io.Serializable {
private Long instrumentId;
private Long legType;
#Column(name = "INSTRUMENT_ID", nullable = false, precision = 22, scale = 0)
public Long getInstrumentId() {
return this.instrumentId;
}
public void setInstrumentId(Long instrumentId) {
this.instrumentId = instrumentId;
}
#Column(name = "LEG_TYPE", nullable = false, precision = 38, scale = 0)
public Long getLegType() {
return this.legType;
}
}
And through audit reader I'm trying to find Audit at particular revision as follows
Session session = HibernateUtil.currentSession();
AuditReader reader = AuditReaderFactory.get(session);
Periodicity periodicity = reader.find( Periodicity.class, instrumentId, revision_Id);
But its giving exception like
org.hibernate.PropertyNotFoundException: Could not locate getter method for property [java.lang.Long#instrumentId]
at org.hibernate.internal.util.ReflectHelper.findGetterMethod(ReflectHelper.java:408)
at org.hibernate.property.access.internal.PropertyAccessBasicImpl.<init>(PropertyAccessBasicImpl.java:41)
at org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl.buildPropertyAccess(PropertyAccessStrategyBasicImpl.java:27)
at org.hibernate.envers.internal.tools.ReflectionTools.getGetter(ReflectionTools.java:53)
Please Help how to access property of Embeddable class..
I'll add what I mentioned in HipChat here for posterity sake.
What you are attemping to do is to use the AuditReader#find method by specifying a specific value of your composite-id class. The method signature that you're using expects the actual embeddable class and not a specific attribute type the embeddable contains.
The proper usage of AuditReader#find would be like:
// define your embeddable class attributes
final PeriodicityId id = new PeriodicityId();
id.setInstrumentId( instrumentId );
// lookup revision 1
final Number revisionId = 1;
// query
final AuditReader auditReader = AuditReaderFactory.get( session );
auditReader.find( Periodicity.class, id, revisionId );
Whlie this avoids the exception you encountered, this won't give you the expected results because the embeddable predicates will assume you're interested in Perodicity instances where the legType attribute is null which is not your goal.
The only way you can accomplish the goal of your task then is to use Envers adhoc query features where you specify the precise predicates to target the results you're interested in.
final AuditReader auditReader = AuditReaderFactory.get( session );
List results = auditReader.createQuery()
.forRevisionsOfEntity( Periodicity.class, true, false )
// add the revision number predicate
.add( AuditEntity.revisionNumber().eq( revisionId ) )
// add the instrument predicate
.add( AuditEntity.property( "id.instrumentId" ).eq( instrumentId ) )
.getResultList();
Hope that helps.

Mapstruct - Send nested entity having (one-to-many relation) in the response

I have 2 entities CallRecords and CallRecordOperators with one-to-many relation as given below
public class CallRecords {
#Id
#Column(name = "id", unique = true)
private String id;
#Column(columnDefinition = "varchar(255) default ''")
private String callerNumber = "";
#OneToMany(mappedBy="callrecord")
private List<CallRecordOperators> callRecordOperators = new ArrayList<CallRecordOperators>();
//getter setters
}
public class CallRecordOperators {
#Id
#Column(name = "id", length = 50, unique = true, nullable = false, insertable = false, updatable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "callRecordId")
private CallRecords callrecord;
#ManyToOne
#JoinColumn(name = "operatorId")
private Operator operator;
#Formats.DateTime(pattern = "yyyy-MM-dd HH:mm:yy")
#Column(columnDefinition = "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP")
private Date startTime = new Date();
#Column(columnDefinition = "varchar(100) default ''")
private String dialStatus;
//getter setter
}
So if the user ask for all "CallRecords" data I also have to give "CallRecordOperators" as they are related.
Current code for Mapper and DTOs
#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface CallRecordsMapper {
CallRecordsMapper INSTANCE = Mappers.getMapper(CallRecordsMapper.class);
#Mapping(source="callRecordOperators",target = "operators")
CallRecordsDto callRecordsToCallRecordsDto(CallRecords callRecords);
public abstract CallRecordOperatorsDto toTarget(CallRecordOperators source);
List<CallRecordsDto> callRecordsToCallRecordsDtos(List<CallRecords> callRecords);
}
public class CallRecordsDto {
private String callerNumber;
private List<CallRecordOperatorsDto> operators;
//getter setters
}
public class CallRecordOperatorsDto {
private String callRecordsId;
private String operatorId;
private String operatorName;
private String currentTime;
// getter setter
}
But for above code I am getting
{
"callerNumber": "9898989898",
"operators": [{
"callRecordsId": null,
"operatorId": null,
"operatorName": null,
"currentTime": null
}, {
"callRecordsId": null,
"operatorId": null,
"operatorName": null,
"currentTime": null
}]
}
the values of operator array are null. what could be he issue?
It seems your are lacking the mappings from CallRecordOperators to CallRecordOperatorsDto:
#Mapper
public interface CallRecordsMapper {
CallRecordsMapper INSTANCE = Mappers.getMapper(CallRecordsMapper.class);
#Mapping(source="callRecordOperators",target = "operators")
CallRecordsDto callRecordsToCallRecordsDto(CallRecords callRecords);
#Mapping(target = "callRecordsId", source = "callrecord.id")
#Mapping(target = "operatorId", source = "operator.id")
#Mapping(target = "operatorName", source = "operator.name")
#Mapping(target = "currentTime", source = "startTime")
CallRecordOperatorsDto callRecordOperatorsToDto(CallRecordOperators source);
}
When you do a Hibernate query of A elements, you can fetch the related B elements of the bs collection using different strategies. Some of them are:
If you use HQL to construct your queries, you can do a JOIN FETCH or LEFT JOIN FETCH to populate the bs collection:
String hql = "SELECT DISTINCT a FROM " + A.class.getName()
+ " a LEFT JOIN FETCH a.bs WHERE ...";
This query will load all data using a single SQL query.
Use eager fetching of the bs collection, changing the #OneToMany annotation:
#OneToMany(fetch=FetchType.EAGER)
private List<B> bs;
In this case, when you run a query of A elements, a SQL query will be launched to retrieve the A data, and for each A object in the result, a SQL query will be executed to load the corresponding bs collection.
If you use Criteria to build the query, you can change the fetch mode of the bs collection in a way similar to the HQL JOIN FETCH:
Criteria c = session.createCriteria(A.class);
c.setFetchMode("bs", FetchMode.JOIN);
c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
How about switching to a slightly different approach that also performs better? By using Blaze-Persistence Entity Views you can define your mapping directly on the DTO classes and apply that onto a query builder to generate efficient queries that perfectly fit your DTO structure.
#EntityView(CallRecords.class)
public interface CallRecordsDto {
// The id of the CallRecords entity
#JsonIgnore
#IdMapping("id") String getId();
String getCallerNumber();
#Mapping("callRecordOperators")
List<CallRecordOperatorsDto> getOperators();
}
#EntityView(CallRecordOperators.class)
public interface CallRecordOperatorsDto {
// The id of the CallRecordOperators entity
#JsonIgnore
#IdMapping("id") Long getId();
#Mapping("callrecord.id")
String getCallRecordId();
#Mapping("operator.id")
String getOperatorId();
#Mapping("operator.name")
String getOperatorName();
#Mapping("startTime")
String getCurrentTime();
// Whatever properties you want
}
See how you can map the entity attributes right in your DTOs? And here comes the code for querying
EntityManager entityManager = // jpa entity manager
CriteriaBuilderFactory cbf = // query builder factory from Blaze-Persistence
EntityViewManager evm = // manager that can apply entity views to query builders
CriteriaBuilder<User> builder = cbf.create(entityManager, CallRecords.class)
.where("callerNumber").eq("123456789");
List<CallRecordsDto> result = evm.applySetting(
builder,
EntityViewSetting.create(CallRecordsDto.class)
).getResultList();
Note that this will roughly generate the following optimized query
SELECT
c.id,
c.callerNumber,
o.callrecord.id,
o.id,
o.startTime,
op.id,
op.name
FROM CallRecords c
LEFT JOIN c.callRecordOperators o
LEFT JOIN o.operator op
WHERE c.callerNumber = :param_1

Spring Data - No property after found for type java.util.Date

I've been struggling with something for the last 2 days and can't fugure it up.
I'm using Java 7, Spring data jpa 1.0 and Hibernate 4.3.1.Final.
I'm trying to make a query that would fetch based on date. I've trimmed the code to make readable
All dates are java.util.Date
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Informe {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "ID")
private Long id;
#OneToOne(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="COMUNICACION_ID")
private Comunicacion comunicacion;
public Comunicacion getComunicacion() {
return comunicacion;
}
public void setComunicacion(Comunicacion comunicacion) {
this.comunicacion = comunicacion;
}
}
#Entity
#Table(name = "REGISTRO_CALIFICACION")
public class RegistroCalificacion extends Informe {
}
#Entity
#Table(name = "COMUNICACION")
public class Comunicacion {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Basic
#javax.persistence.Column(name = "FECHA_PUBLICACION_DESDE", nullable = false, insertable = true, updatable = true, length = 19, precision = 0)
private Date fechaPublicacionDesde;
public Date getFechaPublicacionDesde() {
return fechaPublicacionDesde;
}
public void setFechaPublicacionDesde(Date fechaPublicacionDesde) {
this.fechaPublicacionDesde = fechaPublicacionDesde;
}
}
public interface InformeRepository extends BaseRepository<Informe, Long> {
List<RegistroCalificacion> findByComunicacionFechaPublicacionDesdeAfter(Date date);
}
This throws this exception while creating the beans
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property after found for type java.util.Date
But If I access directly to ComunicaciĆ³n, like below, it works fine.
public interface ComunicacionRepository extends BaseRepository<Comunicacion, Long> {
List<Comunicacion> findByFechaPublicacionDesdeBefore(Date date);
}
I've tried using #Query and I get the same result.
Any thoughts?
I think this should be your repository:
public interface RegistroCalificacionRepository extends BaseRepository<RegistroCalificacion, Long> {
List<Comunicacion> findByFechaPublicacionDesdeBefore(Date date);
}
Because you want the RegistroCalificacion to be queried not Comunicacion.
Could you add
#Temporal(TemporalType.DATE)
#javax.persistence.Column(name = "FECHA_PUBLICACION_DESDE", nullable = false, insertable = true, updatable = true, length = 19, precision = 0)
#Temporal(TemporalType.DATE)
private Date fechaPublicacionDesde;
It should be:
public interface InformeRepository extends BaseRepository<Informe, Long> {
List<RegistroCalificacion> findByComunicacionFechaPublicacionDesde(Date date);
}
You are adding the word "After" at the end of the name of your method, and the repository can not find a property named like that on your "Comunicacion" entity.

Could EclipseLink be causing a ORA-00957: duplicate column name error because of a #DiscriminatorColumn in a polymorphic entity?

I'm getting this exception in an attempt to persist an entity object in an Oracle database, and I only started getting this error after switching my JPA project to EclipseLink 2.0 from Hibernate, and I'm using "entity inheritance" if this could have anything to do with it (which I highly suspect).
*
Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: ORA-00957: duplicate column name
Error Code: 957
Call: INSERT INTO SUREC (ID, PERSON_ID, SURECID, VERSIYONNO, FAZ, FORM_TARIH, DURUMKODU_ID, surecId) VALUES (...
*
The exception message suggests that SURECID is generated twice in the SQL which seems to be causing the duplicate column error, however surecId is defined once as a property and annotated as a discriminator column in the entity class: (see below)
The base entity class resembles:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name = "surecId")
public class Surec implements java.io.Serializable {
private static final long serialVersionUID = -6008473677883005878L;
#Column(name = "ID")
private Long id;
#Basic(optional = false)
#Column(name = "FAZ")
private int faz;
#Basic(optional = false)
#Column(name = "FORM_TARIH")
#Temporal(TemporalType.DATE)
private Date formTarih;
#Column(name = "PERSON_ID")
private Integer personId;
// #Column(name = "SURECID", updatable = false, length=17)
#Column(updatable = false, length=17)
private String surecId;
#Column(name = "VERSIYONNO")
private Long versiyonno;
#JoinColumn(name = "DURUMKODU_ID", referencedColumnName = "ID")
#ManyToOne
private DurumKod durumKodu;
public Surec() {
}
public Surec(String surecId) {
this.surecId = surecId;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
.
.
.
public String getSurecId() {
return surecId;
}
public void setSurecId(String surecId) {
this.surecId = surecId;
}
.
.
.
I commented the "#Column(name=..." annotation jus to see if it could be causing the duplicate column error, but it didn't work out.
And below is the polymorphic entity extending Surec.java above:
#Entity
#DiscriminatorValue("atf")
public class MailOrder extends Surec {
private static final long serialVersionUID = 8333637555543614502L;
#Column(name = "AMOUNT")
private Double amount;
#Basic(optional = false)
#Column(name = "CURRENCY", length = 17)
private String currency;
#Column(name = "BANK")
private String Bank;
#Column(name = "ACCOUNT_ID", length = 31)
private String accountId;
#Column(name = "INVOICE_ID")
private Integer invoiceId;
public MailOrder() {
}
public MailOrder(String surecId) {
super(surecId);
}
public String getCurrency() {
return currency;
}
.
.
.
The error occurs when I try to persist this very sub-entity.
It doesn't override any property of its superclass, although I'm not sure if it's the constructor...
Any advise to resolve the problem (and acknowledgement of any possible EclipseLink or Oracle (or my!) bug will be appreciated.
This is a common issue if you have a relationship mapping that uses this field, and has to do with case sensitivity. Try indicating the descriminator colum as
#DiscriminatorColumn(name = "SURECID")
EclipseLink is case sensitive by default, which is why surecId is seen as a different field from SURECID. You can make EclipseLink case insensitive by using the eclipselink.jpa.uppercase-column-names property, which when set to true, forces EclipseLink to use the upper case on field name comparisons.
I guess you need to mark the property as insertable = false, updateable = false, since it's already inserted as a discriminator:
#Column(insertable = false, updatable = false, length=17)
private String surecId;

Categories

Resources