JPA Query gets transformed [duplicate] - jpa

This question already has answers here:
Spring Boot + JPA : Column name annotation ignored
(13 answers)
Closed 6 years ago.
I have a repository method like
#Query("from Application app where app.id.hostId = :hostId")
List<Application> getApplicationsByApplicationId(#Param("hostId") String hostId);
And my domain class reads something like
public class Application {
#EmbeddedId
Composite id;
...
...
#Embeddable
public static class Composite implements java.io.Serializable {
#Column(name = "id")
private String id;
#Column(name = "hostId")
private String hostId;
....
Somewhere down the processing chain, hostIdstring gets transformed to host_id, So I get an error like
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'application0_.host_id' in 'field list'.
I am trying to migtate an existing hibernate / JPA layer into Spring Boot JPA.
Any pointers will be greatly appreciated.

This is caused by the Hibernate naming strategy.
The default strategy transforms camel case in Java to _ in database.
If you don't want this behavior use JPA default. You can set it in application.properties:
spring.jpa.hibernate.naming_strategy: org.hibernate.cfg.EJB3NamingStrategy

Use:
#Column(name = "hostid")
notice that all are lowercase.

Related

#Audited table and byte[] #Lob field problem

I have adudited table with #Lob field. Without #Audited saving object by Spring CrudRepository works ok, but when i want audit turn on i get error: PSQLException: ERROR: column "content" is of type oid but expression is of type bytea. How to resolve this ? Content column in PostgreSQL database is oid type (for both tables). On Hibernate 5.x the same configuration works, but not o Hibernate 6.x.
#Entity
#Audited
#Table(name = "up_test")
#Getter #Setter
public class UploadTestEntity extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "up_test_seq")
#TableGenerator(table = "id_generator", name = "up_test_seq", initialValue = 1, allocationSize = 1)
private Integer id;
#Lob
private byte[] content;
}
Just remove the #Lob annotation.
The Postgres JDBC driver does not support handling the bytea type via the JDBC LOB APIs setBlob()/getBlob(). I don't know why, and it seems like something that should be supported.
But on the other hand, you don't need it here. The most natural way to handle a field of type byte[] mapping to bytea is to use setBytes()/getBytes(), which is the job of Hibernate's VarbinaryJdbcType.
I don't know where people got the idea that they needed to use #Lob for this instead of just going with the default mapping for byte[].

Alternative initial value for entity in JPA

I have the following entity and their mapping
#Entity
#Table(name = "test")
public class Test implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator", initialValue = 20000)
I want id value begins in 20000 but it doesn't work in postgresql. When the application starts I receive this exception
Caused by: org.hibernate.HibernateException: Multiple references to
database sequence [sequence_generator] were encountered attempting to
set conflicting values for 'initial value'. Fou nd [20000] and [1]
Do I need a extra configuration to it works?
PS: It is a new database without any previous configuration
You could try #TableGenerator annotation. Initial values can be set and seed other values.
You can find example and documentation here:
Set Initial Value Of Table Generator

Hibernate Search and composed key using #IdClass

I have a problem to integrate Hibernate Search in existing project with hundreds of entities but at least half of entities use #IdClass annotation as composed key. Can I solve the problem using the annotation #IdClass?
I also read this post Hibernate search and composed keybut I have not managed to solve my problem.
I have the following example:
entity class:
#Entity
#Table(name="FAKVS_DB")
#IdClass(value=PK_FAKVS_DB.class)
#Audited
#Indexed
public class FAKVS_DB implements Serializable {
#Id
#Column(name="Key_FAM", length=10, nullable=false)l
private String keyFam;
#Id
#Column(name="Komponentennr", nullable=false)
private Integer komponentenNr;
#Id
#Column(name="Hinweis", nullable=true, length=4)
private String hinweis;
//getters and setters
}
and composed key:
public class PK_FAKVS_DB implements Serializable {
private String keyFam;
private Integer komponentenNr;
private String hinweis;
//getters and setters
}
The error that occurs is:
HSEARCH000058: HSEARCH000212: An exception occurred while the MassIndexer was transforming identifiers to Lucene Documents
java.lang.ClassCastException: package.entities.module.fi.pk.PK_FAKVS_DB cannot be cast to java.lang.Integer
at org.hibernate.type.descriptor.java.IntegerTypeDescriptor.unwrap(IntegerTypeDescriptor.java:36)
at org.hibernate.type.descriptor.sql.IntegerTypeDescriptor$1.doBind(IntegerTypeDescriptor.java:63)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:90)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:286)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:281)
at org.hibernate.loader.Loader.bindPositionalParameters(Loader.java:1995)
at org.hibernate.loader.Loader.bindParameterValues(Loader.java:1966)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1901)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1862)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1839)
at org.hibernate.loader.Loader.doQuery(Loader.java:910)
If I can not use #IdClass annotation can you tell me what are the alternatives?
Thank you very much in advance.
An alternative is to add a new property to be used as Id by Hibernate Search. You can mark this with #DocumentId to have the Hibernate Search engine treat the alternative property as the identifier in the index.
You will need to ensure that this new property is unique of course; this can typically done by generating a String from the real id. You probably want to annotate the new getter with #Transient so that it doesn't get persisted in the database.

EclipseLink MultiTenant and Spring Data JPA - #IdClass annotation required - Why?

I'm developing a multi-tenant (multi-schema) application using Spring-Data-JPA and EclipseLink.
When not using multi-tenant capabilities everything is ok, JPA entity works as a charme and obviously works with only one schema.
When I try to activate the multi-tenant adding the folloqing annotation to the entity :
#Multitenant(value=MultitenantType.TABLE_PER_TENANT)
#TenantTableDiscriminator(type=TenantTableDiscriminatorType.SCHEMA, contextProperty="eclipselink-tenant.id")
and I restart the application, i get the following exception :
Caused by: java.lang.IllegalArgumentException: No #IdClass attributes exist on the IdentifiableType [EntityTypeImpl#15818739:CrsMomiJob [ javaType: class com.gpdati.momi.model.core.CrsMomiJob descriptor: RelationalDescriptor(com.gpdati.momi.model.core.CrsMomiJob --> [DatabaseTable(CRS_MOMI_JOB)]), mappings: 7]]. There still may be one or more #Id or an #EmbeddedId on type.
at org.eclipse.persistence.internal.jpa.metamodel.IdentifiableTypeImpl.getIdClassAttributes(IdentifiableTypeImpl.java:169)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation$IdMetadata.<init>(JpaMetamodelEntityInformation.java:170)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:71)
at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:65)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:146)
at com.gpdati.momi.jpa.MultiTenantJpaRepositoryFactory.getTargetRepository(MultiTenantJpaRepositoryFactory.java:30)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:67)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:136)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:153)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:43)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
... 79 more
It seems like the #Id annotation on the Id field is no more read from Spring-Data that look for a #IdClass annotation (I thought #IdClass annotation is required when using a composite primary key, that's not my case)
Any clue?
Thanks!
Here the full entity code :
#Entity
#Table(name="CRS_MOMI_JOB")
#Multitenant(value=MultitenantType.TABLE_PER_TENANT)
#TenantTableDiscriminator(type=TenantTableDiscriminatorType.SCHEMA, contextProperty="eclipselink-tenant.id")
public class CrsMomiJob implements Serializable {
private static final long serialVersionUID = -432489894772L;
private String abilitata;
#Column(name="HOT_CODICE")
private String hotCodice;
#Column(name="INT_CODICE")
private String intCodice;
private Long intervallo;
private String note;
private String parametri;
#Id
private BigDecimal id;
public CrsMomiJob() {
}
... all getters and setters ...
}
Seems to be a bug in the EclipseLink meta model code in hasSingleIdAttribute(), this is returning true (as the id is composite for multitenants) but this should be hidden, so should be returning false.
Please log a bug.

Why is this JPA 2.0 mapping giving me an error in Eclipse/JBoss Tools?

I have the following situation:
(source: kawoolutions.com)
JPA 2.0 mappings (It might probably suffice to consider only the Zip and ZipId classes as this is where the error seems to come from):
#Entity
#Table(name = "GeoAreas")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.STRING)
public abstract class GeoArea implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
protected Integer id;
#Column(name = "name")
protected String name;
...
}
#Entity
#Table(name = "Countries")
#DiscriminatorValue(value = "country")
public class Country extends GeoArea
{
#Column(name = "iso_code")
private String isoCode;
#Column(name = "iso_nbr")
private String isoNbr;
#Column(name = "dial_code")
private Integer dialCode = null;
...
}
#Entity
#Table(name = "Zips")
#IdClass(value = ZipId.class)
public class Zip implements Serializable
{
#Id
#Column(name = "code")
private String code;
#Id
#ManyToOne
#JoinColumn(name = "country_code", referencedColumnName = "iso_code")
private Country country = null;
...
}
public class ZipId implements Serializable
{
private String country;
private String code;
...
}
Pretty simple design:
A country is a geo area and inherits the ID from the root class. A ZIP code is unique within its country so it combines an ISO code plus the actual ZIP code as PK. Thus Zips references Countries.iso_code, which has an alternative unique, not-null key on it (reference to non-primary key column!). The Zip.country association gets an #Id annotation and its variable name is the same as the one in its ID class ZipId.
However I get this error message from within Eclipse (also using JBoss Tools):
Validation Message:
"The attribute matching the ID class attribute country does not have the correct type java.lang.String"
Why is this wrong in JPA 2.0 syntax (see #Id annotation on Zip.country)? I don't think it is. After all the types of Zip.country and ZipId.country can't be the same for JPA 2 because of the #Id annotation on the #ManyToOne and the PK being a simple integer, which becomes the ID class counterpart. Can anyone check/confirm this please?
Could this be a bug, probably in JBoss Tools? (Which software component is reporting the above bug? When putting the 3 tables and entity classes into a new JavaSE project there's no error shown with the exact code...)
Answering own question...
The way I modeled the reference, I use a String because the FK points to the iso_code column in the Countries table which is a CHAR(2), so basically my mapping is right. However, the problem is that JPA 2.0 doesn't allow anything but references to primary key columns. This is what the Eclipse Dali JPA validator shows.
Taken from "Pro JPA 2.0" by Keith/Schincariol p.283 top, "Basic Rules for Derived Identifiers" (rule #6): "If an id attribute in an entity is a relationship, then the type of the matching attribute in the id class is of the same type as the primary key type of the target entity in the relationship (whether the primary key type is a simple type, an id class, or an embedded id class)."
Personal addendum:
I disagree with JPA 2.0 having this limitation. JPA 1.0 mappings allow references to non-PK columns. Note, that using JPA 1.0 mappings instead isn't what I'm looking for. I'd rather be interested in the reason why this restriction was imposed on JPA 2.0. The JPA 2.0 is definitely limiting.
I'd say focus your attention on the CompoundIdentity relationship. See this question, and my answer there
Help Mapping a Composite Foreign Key in JPA 2.0
ZipId has no "country" field in your case
I have not tested your code, but it looks pretty much related to the use of the #PrimareKeyJoinColumn annotation.
The JPA 2.0 specification in section 11.1.40 states:
The PrimaryKeyJoinColumn annotation is
used to join the primary table of an
entity subclass in the JOINED mapping
strategy to the primary table of its
superclass; it is used within a
SecondaryTable annotation to join a
secondary table to a primary table;
and it may be used in a OneToOne
mapping in which the primary key of
the referencing entity is used as a
foreign key to the referenced
entity[108].
The example in the spec looks like your case.
#Entity
#Table(name="CUST")
#Inheritance(strategy=JOINED)
#DiscriminatorValue("CUST")
public class Customer { ... }
#Entity
#Table(name="VCUST")
#DiscriminatorValue("VCUST")
#PrimaryKeyJoinColumn(name="CUST_ID")
public class ValuedCustomer extends Customer { ... }
I hope that helps!