hibernate name convention failing - error is ORA-00904 - invalid identifier - sql

I am not able to provide the proper identifier that resolved properly at hibernate layer.
actualy coulmn AD_USR_ACC_ID
but in hibernate query it is getting modified like this - adUsrAccId
i even used naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy but no luck
#Entity
#Table(name = "OWNER.SAMPLE_TABLE")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class SampleTableRepo implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "AD_USR_ACC_ID")
private Integer adUsrAccId;
converted query like this -
Hibernate:
select
sampleprd0_.adUsrAccId as adUsrAccId9_1_0_
from
OWNER.SAMPLE_TABLE sampleprd0_
where
sampleprd0_.adUsrAccId=?
Error is -
{"message":"could not extract ResultSet [n/a]","timestamp":1629212973851,"log.level":"DEBUG","logger.name":"org.hibernate.engine.jdbc.spi.SqlExceptionHelper","thread.name":"http-nio-8080-exec-1","error.class":"java.sql.SQLSyntaxErrorException","error.message":"ORA-00904: \"SAMPLEPRD0_\".\"ADUSRACCID\": invalid identifier\n","error.stack":" at

Related

Hibernate JPQL query is very slow compared to SQL

In my project I have a problem with one JPQL query which takes about 1,5s. When I execute SQL query copied from debug log (the same one which executes Hibernates) direct on PostgreSQL db it takes about 15ms.
#Service
#Transactional
#Slf4j
public class PersonSyncServiceImpl
extends HotelApiCommunicationService implements PersonSyncService {
[...]
private PersonLinked getLinkedPerson(CNPerson cnPerson, Obiekt obiekt) {
PersonLinked person = cnPerson.getPersonLinked();
if (person == null) {
var o = cnPerson;
List<PersonLinked> personList = personLinkedDao.findPersonLinkedByRecordData(
o.getPersonImiona(), o.getPersonNazwisko(), o.getPersonPesel(), o.getPersonEmail(), o.getPersonTelefon1());
person = personList.stream()
.findFirst().orElse(null);
if (person == null) {
person = createPersonLinkedFromCnPerson(cnPerson);
personLinkedDao.save(person);
}
cnPerson.setPersonLinked(person);
}
return person;
}
[...]
}
Problem is with this line:
List<PersonLinked> personList = personLinkedDao.findPersonLinkedByRecordData(
o.getPersonImiona(), o.getPersonNazwisko(), o.getPersonPesel(), o.getPersonEmail(), o.getPersonTelefon1());
Dao with defined query:
#Repository
#Transactional
public interface PersonLinkedDao extends JpaRepository<PersonLinked, Long> {
#Query("select o from PersonLinked o \n" +
"where o.personImiona = :imie and o.personNazwisko = :nazwisko \n" +
" and (o.personPesel = :pesel or o.personEmail = :email or o.personTelefon1 = :telefon)")
List<PersonLinked> findPersonLinkedByRecordData(
#Param("imie") String personImiona,
#Param("nazwisko") String personNazwisko,
#Param("pesel") String personPesel,
#Param("email") String personEmail,
#Param("telefon") String personTelefon);
}
SQL from Hibernate debug log:
select [..]
from
person personlinke0_
where
personlinke0_.person_imiona=?
and personlinke0_.person_nazwisko=?
and (
personlinke0_.person_pesel=?
or personlinke0_.person_email=?
or personlinke0_.person_telefon1=?
)
When I execute this query on database it takes about 15ms, execution from code takes about 1,5s. I commented out this line in code and lag disappeared, so for sure problem is this jpql select.
Database connection configuration:
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.datasource.url=jdbc:postgresql://192.168.1.200:5433/XXXXXXX
spring.datasource.username=XXXXX
spring.datasource.password=XXXXX
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.generate_statistics=true
UPDATE 1:
debug.log:
26-09-2020 16:06:36.130 [http-nio-8091-exec-2] DEBUG org.hibernate.SQL.logStatement -
select [...]
from
person personlinke0_
where
personlinke0_.person_imiona=?
and personlinke0_.person_nazwisko=?
and (
personlinke0_.person_pesel=?
or personlinke0_.person_email=?
or personlinke0_.person_telefon1=?
)
26-09-2020 16:06:36.130 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.doGetTransaction - Found thread-bound EntityManager [SessionImpl(1971671100<open>)] for JPA transaction
26-09-2020 16:06:36.130 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.handleExistingTransaction - Participating in existing transaction
26-09-2020 16:06:36.146 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.doGetTransaction - Found thread-bound EntityManager [SessionImpl(1971671100<open>)] for JPA transaction
26-09-2020 16:06:36.146 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.handleExistingTransaction - Participating in existing transaction
26-09-2020 16:06:36.146 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.doGetTransaction - Found thread-bound EntityManager [SessionImpl(1971671100<open>)] for JPA transaction
26-09-2020 16:06:36.146 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.handleExistingTransaction - Participating in existing transaction
26-09-2020 16:06:37.521 [http-nio-8091-exec-2] DEBUG org.hibernate.SQL.logStatement -
UPDATE 2:
PersonLinked entity class:
#Entity
#Table(name = "person")
#Getter
#Setter
#SuperBuilder
#EqualsAndHashCode(of = "personId")
public class PersonLinked extends SCPerson {
#Id
#GeneratedValue(generator = "seq_person", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "seq_person", sequenceName = "seq_person", allocationSize = 30)
#Column(name = "OSOBA_ID", nullable = false)
private Long personId;
#OneToMany(mappedBy = "personLinked", fetch = FetchType.LAZY)
private List<CNPerson> cnPersonList;
#Tolerate
public PersonLinked() {
super();
}
#PrePersist
#Override
protected void preInsert() {
super.preInsert();
}
}
SCPerson class:
#MappedSuperclass
#Getter
#Setter
#SuperBuilder
public class SCPerson {
[...]
}
Finally I found a solution, problem was in another part of code.
Before calling method getLinkedPerson() I had this line of code:
List<CNPerson> cnPersonList = cnPersonDao.findCnPersonNotLinkedWithPerson(obiekt.getLoid());
cnPersonList constans here about 70 000 objects.
I changed it to:
List<Integer> ids = cnPersonDao.findCnPersonIdsNotLinkedWithPerson(obiekt.getLoid());
Problem is described here: https://stackoverflow.com/a/46258045/9678458
Slow down during Hibernate context refresh. In case when you update
too many objects ORM engine (lets say Hibernate) has to sink them and
keep them in memory. Literally Hibernate must have all old states and
all new states of updated objects. Sometimes it does this quite
unoptimal.
You can indicate this using debug. Try to find the slowest place and
check what exactly is being invoked there. I might guess that it slows
down when hibernate updates state of the cache.
I think it is because entities CNPerson and PersonLinked are linked, but I am not sure:
#ManyToOne(fetch = FetchType.LAZY,
cascade = {CascadeType.MERGE, CascadeType.PERSIST})
#JoinTable(name = "cnperson_links",
joinColumns = {#JoinColumn(name = "cnperson_loid")},
inverseJoinColumns = {#JoinColumn(name = "person_id")})
private PersonLinked personLinked;

Spring Data JPA: CriteriaQuery to get entities with max value for each unique foreign key

There's an Event class:
#Entity
public class Event {
#Id
private Integer id;
#ManyToOne(cascade = CascadeType.ALL)
private Company company;
#Column
private Long time;
...
}
I want to have an EventFilter class (implementing Specification) which will produce CriteriaQuery to select entities the same way as the following SQL query:
SELECT *
FROM events e1
WHERE e1.time = (
SELECT MAX(time)
FROM events e2
WHERE e1.company_id = c2.company_id
)
Filtered result will contain only events with unique Company and max time value per company.
This is the EventFilter class with what I ended up with:
public class EventFilter implements Specification<Event> {
#Override
public Predicate toPredicate(Root<Event> root, CriteriaQuery<?> q, CriteriaBuilder cb) {
Subquery<Long> subquery = q.subquery(Long.class);
Root<Event> subRoot = subquery.from(Event.class);
subquery.select(cb.max(root.get("time")))
.where(cb.equal(root.get("company"), subRoot.get("company")));
return cb.equal(root.get("time"), subquery);
}
}
When EventRepository#findAll(EventFilter filter) is called, results are not filtered at all. Please help me to implement this logic correctly.
After inspecting SQL statement generated by Hibernate I've found an error: root was used instead of subRoot. The correct method body is:
Subquery<Long> sub = q.subquery(Long.class);
Root<Event> subRoot = sub.from(Event.class);
sub.select(cb.max(subRoot.get("time")))
.where(cb.equal(root.get("company"), subRoot.get("company")));
return cb.equal(root.get("time"), sub);

SQLNative query returning empty results

I'm trying to execute a query which needs 4 tables :
#Query(value="SELECT e.* FROM erreur e, synop sy, synop_decode sd, station st WHERE e.id_synop = sd.id_synop_decode "
+ "and sd.id_synop_decode = sy.id_synop" + " and DATE(sy.date)= :date and "
+ "sy.id_station = st.id_station and st.id_station= :stationId", nativeQuery=true)
public List<Erreur> recherche(#Param("date") Date date, #Param("stationId") Long stationId);
This query works fine et native sql, i pass an existing stationId and a date like the following :
SELECT e.* FROM erreur e, synop sy, synop_decode sd, station st WHERE e.id_synop = sd.id_synop_decode and sd.id_synop_decode = sy.id_synop
and DATE(sy.date)= '2019-05-27' and sy.id_station = st.id_station and st.id_station= 60355;
This query works fine in Mysql Workbench.
Here's the actual controller i'm using for testing purpose :
#GetMapping("/station/{stationId}/erreurs/today")
public List<Erreur> getTodayErreurByStationId(#PathVariable Long stationId)
{
List<Erreur> erreurs = new ArrayList<Erreur>();
Optional<Station> stationOptional = stationRepository.findById(stationId);
if(stationOptional.isPresent())
{
return erreurRepository.recherche(new Date(), stationId);
}
return null;
}
The expected results are the actual "Ererur" objects in my array list, but RestClient just returns an empty array [], while the query works just fine in mysql like i described it above.
So my question is : How can i write this query into Hql language so that i can return the right entities. Or how can i map my sql results to my target custom calss "Erreur"?
#Entity
#Getter #Setter #NoArgsConstructor
#Table(name="erreur")
public class Erreur {
public Erreur(int section, int groupe, String info) {
this.section = section;
this.groupe = groupe;
this.info = info;
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id_erreur")
private Long id;
#ManyToOne(cascade= {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH},
fetch=FetchType.LAZY)
#JsonIgnore
#JoinColumn(name="id_synop")
private SynopDecode synopDecode;
#OneToOne
#JoinColumn(name="id_controle")
private Controle controle;
#ManyToOne(cascade= {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH},
fetch=FetchType.LAZY)
#JsonIgnore
#JoinColumn(name="id_station")
private Station station;
#Column(name="section")
private int section;
#Column(name="groupe")
private int groupe;
#Column(name="info")
private String info;
}
If you want to use jpa convention directly then you will have to make associations between different entities i.e. how two entities are linked. When we define these associations then spring jpa knows how to convert method name or custom queries into SQL.
Your code will need to be something like
public class Erreur {
...
#ManyToOne
#JoinColumns//define how Erreur and SynopeDecone are linked
private SynopDecode synopDecode;
...
public class SynopDecode {
...
#ManyToOne // or #OneToOne its not mentioned in question how these two are linked
#JoinColumns//define how SynopDecode and Synop are linked
private Synop synop;
...
Then you can write your query like
#Query("select e from Erreur e LEFT JOIN e.synopDecode sy LEFT JOIN sy.synop sy WHERE DATE(sy.date) = :date AND sy.id_station = :stationId")
List<Erreur> getByDateAndStationId(#Param("date") Date date, #Param("stationId") Long stationId)
You can't use method name based query because you want to use SQL function to match only "date" part of your date and not the whole timestamp.
You can use jpa methods by conventions.
Assuming SynopDecode has property like:
//SynopDecode class
#ManyToOne
private Synop synop;
//repository interface
List<Erreur> findByStationIdAndSynopDecodeSynopDate(Long stationId, Date date);
//or
//List<Erreur> findByStationIdAndSynopDecode_Synop_Date(Long stationId, Date date);
UPDATE
As Punit Tiwan (#punit-tiwan) note that, the above methods used for a specific datettime.
You can use methods below for just DATE.
//repository interface
List<Erreur> findByStationIdAndSynopDecodeSynopDateBetween(Long stationId, Date startOfDate, Date endOfDate);
//or
//List<Erreur> findByStationIdAndSynopDecode_Synop_DateBetween(Long stationId, Date startOfDate, Date endOfDate);
I figured a way to get the same results as my SQL Query using the #Query annotation and accessing object properties like this :
#Query("from Erreur e where e.synopDecode.synop.station.id = :stationId and "
+ "DATE(e.synopDecode.synop.date) = :date")
public List<Erreur> recherche(#Param("date") Date date, #Param("stationId") Long stationId);
I think it solves my problem, thanks for the help

JPA 2.1 Timestamp type field for versioning and optimistic locking always throwing OptimisticLockException

Environment: JPA 2.1, EclipseLink 2.6.3, SQL Server 2016
I want to use a field of type Timestamp for versioning and optimistic. I do not have option to use numeric column for versioning. My understanding is I just need to annotate the field with #Version and that all.
Database Table: token_t
token_id int PK
token_name varchar(100)
last_updt_dtm datetime
Entity Class
#Entity
#Table(name = "token_t")
public class TokenAE {
#Id
#Column(name = "token_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int tokenId;
#Column(name = "token_name")
private String tokenName;
#Version
#Column(name = "last_updt_dtm")
private Timestamp lastUpdtDtm;
// getter/setter omitted to avoid cluttering
}
Test Method
#Test
public void optimisticLockingTest1() throws Exception {
PersistenceHelper.getEntityManager().getTransaction().begin();
TokenAE tokenAE = tokenDAO.getToken(616);
assertNotNull("tokenAE is null", tokenAE);
tokenAE.setTokenName("new token name");
PersistenceHelper.getEntityManager().merge(tokenAE);
PersistenceHelper.getEntityManager().getTransaction().commit();
}
Note - PersistenceHelper is just helper class instantiating entity manager
As you can see, I am loading TokenAE updating name and doing merge. I made sure that underlying database record is not changed. So I am expecting the merge/update should be successful but it always throws OptimisticLockException.
See the stacktrace below. I enabled JPA query/param logging and I can see the UPDATE query and bind parameters. The value of last_updt_dtm in WHERE clause [2018-07-17 22:59:48.847] matches exactly to the value in database record and this UPDATE query should return rowCount 1 and it should be successful.
I have no idea what going on here. Any help is greatly appreciated.
Exception Stacktrace
[EL Fine]: sql: 2018-07-18 23:54:13.137--ClientSession(1451516720)--Connection(1323996324)--Thread(Thread[main,5,main])--
UPDATE token_t SET token_name = ?, last_updt_dtm = ? WHERE ((token_id = ?) AND (last_updt_dtm = ?))
bind => [new token name, 2018-07-18 23:54:13.35, 616, 2018-07-17 22:59:48.847]
[EL Warning]: 2018-07-18 23:54:13.286--UnitOfWork(998015174)--Thread(Thread[main,5,main])--Local Exception Stack:
Exception [EclipseLink-5006] (Eclipse Persistence Services - 2.6.3.v20160428-59c81c5): org.eclipse.persistence.exceptions.OptimisticLockException
Exception Description: The object [TokenAE [tokenId=616, tokenName=new token name, lastUpdtDtm=2018-07-18 23:54:13.35]] cannot be updated because it has changed or been deleted since it was last read.
Class> com.test.TokenAE Primary Key> 616
at org.eclipse.persistence.exceptions.OptimisticLockException.objectChangedSinceLastReadWhenUpdating(OptimisticLockException.java:144)
at org.eclipse.persistence.descriptors.VersionLockingPolicy.validateUpdate(VersionLockingPolicy.java:790)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.updateObjectForWriteWithChangeSet(DatabaseQueryMechanism.java:1086)
at org.eclipse.persistence.queries.UpdateObjectQuery.executeCommitWithChangeSet(UpdateObjectQuery.java:84)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:301)
at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:803)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1790)
at org.eclipse.persistence.internal.sessions.CommitManager.commitChangedObjectsForClassWithChangeSet(CommitManager.java:273)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:131)
at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4264)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1531)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:278)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commit(UnitOfWorkImpl.java:1113)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:137)
at sunlife.us.dc.bds.token.domain.TokenDAOTest.optimisticLockingTest1(TokenDAOTest.java:39)

autoincrement id is not reflecting in composite key using JPA

I have a below mapping
#Entity
#Table(name = "auctions")
public class Auction{
.
.
#OneToMany(cascade = CascadeType.ALL, mappedBy = "auction")
private List<AuctionParamValue> auctionParamValueList;
.
.
}
#Entity
#Table(name = "auction_param_values")
public class AuctionParamValue {
#EmbeddedId
protected AuctionParamValuePK auctionParamValuePK;
#JoinColumn(name = "auction_param_id", referencedColumnName = "auction_param_id",updatable=false,insertable=false)
#ManyToOne #MapsId("auctionParamId")
private AuctionParam auctionParam;
#JoinColumn(name = "auction_id", referencedColumnName = "auction_id",updatable=false,insertable=false)
#ManyToOne #MapsId("auctionId")
private Auction auction;
}
#Embeddable
public class AuctionParamValuePK {
#Id
#Basic(optional = false)
#Column(name = "auction_id")
#Nullable
private Long auctionId = null;
#Id
#Basic(optional = false)
#Column(name = "auction_param_id")
#Nullable
private Long auctionParamId = null;
}
#Entity
#Table(name = "auction_params")
public class AuctionParam {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "auctionParam")
private List<AuctionTypeParam> auctionTypeParamList;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "auctionParam")
private List<AuctionParamValue> auctionParamValueList;
}
}
When I try to persist auction (I am manually setting the auctionParamId and expecting the auctionId to be automaticlly set (may be the last inserted id) )
but I am getting below error, I am not sure why the auctionId in the query is going as 0 instead of latest id in the auction.(I am using eclipselink jpa provider)
Internal Exception: com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`portaldemo`.`auction_param_values`, CONSTRAINT `auction_param_values_auction_id_fk` FOREIGN KEY (`auction_id`) REFERENCES `auctions` (`auction_id`))
Error Code: 1452
Call: INSERT INTO auction_param_values (auction_param_val, create_ts, last_updt_ts, auction_param_id, auction_id) VALUES (?, ?, ?, ?, ?)
bind => [2011-02-12 04:00:00, 2011-01-27 12:02:00.28, 2011-01-27 12:17:43.25, 2, 0]
Query: InsertObjectQuery(com.eaportal.domain.AuctionParamValue[auctionParamValuePK=com.eaportal.domain.AuctionParamValuePK[auctionId=0, auctionParamId=2]])
Here the [auctionId=0 is always comming as 0 and not the last inserted id :(
What is theproblem with this mapping ?
An #GeneratedValue will only set the value of the attribute it is annotated on, if you have other attributes in other classes that reference the id you are responsible for setting these.
i.e. you would need to first persist and flush the Auction, and then create the AuctionParamValue using its generate Id.
Or, if you used TABLE or SEQUENCE id generation then you would just need to call persist, and not the flush. In general I would never recommend IDENTITY sequencing as its values cannot be preallocated.
But really you should not have the duplicate fields as all. Remove the #EmbeddedId auctionParamValuePK entirely and just add #Id to the two #ManyToOnes, and use an #IdClass instead. This will make things much simplier and will just work, even with IDENTITY id generation.
You could also instead remove the insertable/updateable=false on the two #ManyToOne mappings and instead put them on the #EmbeddedId attributes, this will have the foreign key written from the relationships, but your object will still be corrupt in memory.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Primary_Keys_through_OneToOne_and_ManyToOne_Relationships
You could try two things:
make the two ids nullable: Use wrapper Types instead of primitives (Integer, Long), and set it to null before saving
leave the combinded Primary ID field (auctionParamValuePK) empty (null) when you save it.
I don't know if this fix the problem, but I am sure that you need to do at least one of them to get it working.