Passing a parameter in a jpql query select - java

I have a jpql query instanciates a java object in select clause
public List<ChampEtatOT> getEtatOT(Date dateDebut, Date dateFin) {
Query query = em.createQuery("SELECT NEW ChampEtatOT( ot.numero, uo.denominationFr, ot.etat, ot.dateDebutReelle , ot.dateFinReelle, :dateParam1, :dateParam2, :dateParam3) FROM ordre ot JOIN ot.unite uo")
.setParameter("dateParam1", dateDebut, TemporalType.DATE)
.setParameter("dateParam2", dateFin, TemporalType.DATE)
.setParameter("dateParam3", new Date("2015-01-01"), TemporalType.DATE);
return query.getResultList();
}
I put 3 parameters, so i can pass it in the constructor
I get this error
Caused by: Exception [EclipseLink-6137] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.QueryExceptionException Description: An Exception was thrown while executing a ReportQuery with a constructor expression: java.lang.NoSuchMethodException: dz.elit.gmao.commun.reporting.classe.ChampEtatOT.<init>(java.lang.String, java.lang.String, java.lang.String, java.util.Date, java.util.Date)Query: ReportQuery(referenceClass=TravOrdreTravail jpql="SELECT NEW dz.elit.gmao.commun.reporting.classe.ChampEtatOT( ot.numero, uo.denominationFr, ot.etat, ot.dateDebutReelle , ot.dateFinReelle, :dateParam1, :dateParam2, :dateParam3) FROM TravOrdreTravail ot JOIN ot.uniteOrganisationnellle uo")
I think that it's not possible to put parameters in a select clause so does anyone have an idea, the constructor method is as follows:
public ChampEtatOT(String numero, String denominationFr, String etat, Date dateDebutReelle, Date dateFinReelle, Date dateParam1, Date dateParam2, Date dateParam3) {
this.numero = numero;
this.denominationFr = denominationFr;
if (etat.equals("OUV")) {
if (dateDebutReelle.before(dateParam1)) {
etatEntreeSortie = "En instance debut du mois";
} else {
if (dateDebutReelle.before(dateParam2)) {
etatEntreeSortie = "En instance fin du mois";
} else {
if (dateDebutReelle.after(dateParam1) && dateDebutReelle.before(dateParam2)) {
etatEntreeSortie = "Entree/Mois";
}
}
}
}
}

Problem solved, as you suggested bRIMOs Bor it's not possible to pass parameters in a SELECT clause, so i have retreived all the results in a List than filtered the results according to the three dates date1, date2, date3
Query query = em.createQuery("SELECT NEW ChampEtatAteliers"
+ "( ot.numero, uo.denominationFr, ot.etat, ot.dateDebutReelle, ot.dateFinReelle) "
+ "FROM ordre ot JOIN ot.unite uo");
List<ChampEtatAteliers> champEtatAtelierses = query.getResultList();
for (ChampEtatAteliers champEtatAtelierse : champEtatAtelierses) {
if (champEtatAtelierse.getDateDebutReelle().compareTo(date1) >= 0 && champEtatAtelierse.getDateDebutReelle().compareTo(date2) <= 0) {
champEtatAtelierList2.add(new ChampEtatAteliers(champEtatAtelierse.getNumero(), champEtatAtelierse.getDenominationFr(), "Entree/Mois"));
}
if (champEtatAtelierse.getEtat().equals("OUV")) {
if (champEtatAtelierse.getDateDebutReelle().compareTo(date1) < 0) {
champEtatAtelierse.setEtatEntreeSortie("En instance début du mois");
} else {
if (champEtatAtelierse.getDateDebutReelle().compareTo(date2) <= 0) {
champEtatAtelierse.setEtatEntreeSortie("En instance fin du mois");
}
}
}
}

I think that it's not possible to reference a parameter in the contructor.
in your case it throws a NoSuchMethodexeption : it means that, no method with the current signature in your ChampEtatOT class (5 parameters instead of 8 )
you can refer to this answer => Passing a parameter in a jpql query select
So ,try to retrive all data then make a filter method to set all the etatEntreeSortie values inside the ChampEtatOT class of the ResultList

Clearly the JPQL BNF does permit passing parameters as constructor arguments.
constructor_expression ::= NEW constructor_name ( constructor_item {, constructor_item}* )
constructor_item ::= single_valued_path_expression | scalar_expression | aggregate_expression |
identification_variable
scalar_expression ::= simple_arithmetic_expression | string_primary | enum_primary |
datetime_primary | boolean_primary | case_expression | entity_type_expression
string_primary ::= state_field_path_expression | string_literal |
input_parameter | functions_returning_strings | aggregate_expression | case_expression
i.e a scalar_expression can be a string_primary, which can be an input_parameter. So your JPA provider is not meeting the JPA spec and you should raise a bug on it.

Related

My grails project facing error"TransactionRequiredException:no transaction" using domain.save(flush:true).save() it can is saving but updating

package com.fhjony.ocbt
import grails.web.servlet.mvc.GrailsParameterMap
class MemberService {
def save(GrailsParameterMap params) {
Member member = new Member(params)
def response = AppUtil.saveResponse(false, member)
if (member.validate()) {
member.save(true)
if (!member.hasErrors()){
response.isSuccess = true
}
}
return response
}
def update(Member member, GrailsParameterMap params) {
member.properties = params
def response = AppUtil.saveResponse(false, member)
if (member.validate()) {
member.save(flush: true)
if (!member.hasErrors()){
response.isSuccess = true
}
}
return response
}
def getById(Serializable id) {
return Member.get(id)
}
def list(GrailsParameterMap params) {
params.max = params.max ?: GlobalConfig.itemPerPage()
List<Member> memberList = Member.createCriteria().list(params) {
if (params?.colName && params?.colValue) {
like(params.colName, "%" + params.colValue + "%")
}
if (!params.sort) {
order("id", "desc")
}
}
return [list: memberList, count: memberList.totalCount]
}
def delete(Member member) {
try {
member.delete(flush: true,failOnError:true)
} catch (Exception e) {
println(e.getMessage())
return false
}
return true
}
}
Error message:
URI /member/update Class
javax.persistence.TransactionRequiredException Message null Caused by
no transaction is in progress
Line | Method
->> 211 | invoke in org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 188 | invoke in org.grails.core.DefaultGrailsControllerClass | 90 | handle . . . .
. in org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter | 1039
| doDispatch in
org.springframework.web.servlet.DispatcherServlet | 942 | doService
. . . in '' | 1005 | processRequest in
org.springframework.web.servlet.FrameworkServlet | 908 | doPost . .
. . . in '' | 882 | service in '' | 77 |
doFilterInternal in
You want your database interactions to be happening in a transactional context. One simple piece of that is you can mark your service class with #grails.gorm.transactions.Transactional.
Separate from that, and this isn't really related to your question, but passing GrailsParameterMap map around as a method argument is an unusual thing to do. What the right thing to do is depends on some factors in your app and you may want to pass values into your service rather than the whole map but if you really want the whole map in the service, one way to get there is by way of WebAttributes.
import grails.gorm.transactions.Transactional
import grails.web.api.WebAttributes
#Transactional
class MemberService implements WebAttributes {
def serviceMethod() {
// you can access params here because
// WebAttributes provides access to it
Member member = new Member(params)
// ...
}
}

EclipseLink - Subqueries with Criteria Query (JPA)

take the following two tables as given:
Objects with columns objId, objTaxonId
Taxa with columns taxId, taxValidSynonymId, taxName (Note Taxa is plural for Taxon)
if a Taxon is valid, its id and validSynonymId are identical, otherwise they are different. To find all synonyms of a Taxon you 'only' need to find all Taxa where the taxValidSynonymId is filled with the taxId of the valid Taxon
how can i gain all Objects where the Taxon has a given Name (including their Synonyms?)
in SQL this is done in a few lines (and Minutes)
SELECT *
FROM Objects
WHERE objTaxonId IN (
SELECT taxId
FROM Taxa
WHERE taxName LIKE 'Test Taxon 1'
OR taxSynIdTaxon IN(
SELECT taxId
FROM Taxa
WHERE taxName LIKE 'Test Taxon 1'
)
)
i was able to work out the inside part, where i gain the list of Taxa and its Synonyms. Now i need to transform this Query to a Subquery...
String NAME_LIKE = "Test Taxon 1";
EntityManager em = EntityManagerProvider.getEntityManager("TestDB"); // get the EntityManager
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<TaxonImpl> cqObject = cb.createQuery(TaxonImpl.class);//
Root<TaxonImpl> taxonRoot = cqObject.from(TaxonImpl.class);//
Expression<String> taxon_name = taxonRoot.<String> get("taxName");
Predicate where = cb.equal(taxon_name, NAME_LIKE);
// subquery
Subquery<Integer> subQuery = cqObject.subquery(Integer.class);
Root<TaxonImpl> subRoot = subQuery.from(clsImpl);
subQuery.select(subRoot.<Integer> get("taxId"));
subQuery.where(cb.equal(subRoot.<String> get("taxName"), NAME_LIKE));
where = cb.or(where, taxonRoot.get("taxValidSynonymId").in(subQuery));
cqObject.where(where);
Query query = em.createQuery(cqObject);
List<TaxonImpl> result = query.getResultList();
NOTE: the Taxon is Mapped as an many to One relation (target-entity is TaxonImpl)
in my actual application the code (from the subquery) will be dynamically, so a Native Query does not help me.
i figured out how to "transform" the subquery into a query but Eclipselink threw me two errors
the first was forbidden access via field, when i tried the in on a result of TaxonImpl (i tried that first, as in my mapping files the Taxon is mapped as Entity.
so after this i tried to form the SQL 1:1 to JPA.
but Eclipselink generated something weird:
SELECT t0.objIdObject, t0.objAdminCreated, t0.objAdminCreator, t0.objAdminEdited, t0.objAdminEditor, t0.objAdminImport1, t0.objAdminImport2, t0.objAddBool1, t0.objAddBool2, t0.objAddBool3, t0.objAddBool4, t0.objAddBool5, t0.objAddDateTime1, t0.objAddDateTime2, t0.objCommonComments, t0.objCommonDescription, t0.objCommonKeywords, t0.objCommonName, t0.objCommonPublished, t0.objCommonPublishedAs, t0.objCommonStatus, t0.objCommonType, t0.objCommonTypustype, t0.objDetAccuracy, t0.objDetCf, t0.objDetComments, t0.objDetDate, t0.objDetMethod, t0.objDetResult, t0.objAddFloat1, t0.objAddFloat2, t0.objAddFloat3, t0.objAddFloat4, t0.objAddFloat5, t0.objEventAbundance, t0.objEventCollectionMethod, t0.objEventComments, t0.objEventMoreContacts, t0.objEventDateDay1, t0.objEventDate1, t0.objEventDateMonth1, t0.objEventDate2, t0.objEventDateUncertain, t0.objEventDateYear1, t0.objEventEcosystem, t0.objEventHabitat, t0.objEventNumber, t0.objEventPermission, t0.objEventSubstratum, t0.objEventTime1, t0.objEventTime2, t0.objEventWeekNumber, t0.objFlora, t0.objGuidObject, t0.objIOComments, t0.objIODeAccessed, t0.objAddInt1, t0.objAddInt2, t0.objAddInt3, t0.objAddInt4, t0.objAddInt5, t0.objStorageForeignNumber, t0.objStorageNumber, t0.objStorageNumberInCollection, t0.objStorageNumberOld, t0.objStorageNumberPrefix, t0.objAddLkp1, t0.objAddLkp10, t0.objAddLkp2, t0.objAddLkp3, t0.objAddLkp4, t0.objAddLkp5, t0.objAddLkp6, t0.objAddLkp7, t0.objAddLkp8, t0.objAddLkp9, t0.objAddLkpCs1, t0.objAddLkpCs10, t0.objAddLkpCs11, t0.objAddLkpCs12, t0.objAddLkpCs13, t0.objAddLkpCs14, t0.objAddLkpCs15, t0.objAddLkpCs2, t0.objAddLkpCs3, t0.objAddLkpCs4, t0.objAddLkpCs5, t0.objAddLkpCs6, t0.objAddLkpCs7, t0.objAddLkpCs8, t0.objAddLkpCs9, t0.objOriginAccessionDate, t0.objOriginAccessionNumber, t0.objOriginComments, t0.objOriginMoreContacts, t0.objOriginSource, t0.objOriginType, t0.objPreparationComments, t0.objPreparationDate, t0.objPreparationType, t0.objPropAdults, t0.objPropAge, t0.objPropAgeUnit, t0.objPropEggs, t0.objPropFemale, t0.objPropHeight, t0.objPropHeightUnit, t0.objPropJuveniles, t0.objPropLarvae, t0.objPropLength, t0.objPropLengthUnit, t0.objPropMale, t0.objPropObservation, t0.objPropObservationComments, t0.objPropPupae, t0.objPropSex, t0.objPropStadium, t0.objPropWeight, t0.objPropWeightUnit, t0.objPropWidth, t0.objPropWidthUnit, t0.objSiteComments, t0.objStorageComments, t0.objStorageContainerNumber, t0.objStorageContainerPieces, t0.objStorageContainerType, t0.objStorageLevel1, t0.objStorageLevel2, t0.objStorageLevel3, t0.objStorageLevel4, t0.objStorageLevel5, t0.objStorageNumberInContainer, t0.objstoragePieces, t0.objStorageValue, t0.objStorageValueUnit, t0.objAddText1, t0.objAddText10, t0.objAddText2, t0.objAddText3, t0.objAddText4, t0.objAddText5, t0.objAddText6, t0.objAddText7, t0.objAddText8, t0.objAddText9, t0.objIdCollection, t0.objCommonIdReference, t0.objDetIdContact, t0.objDetIdReference, t0.objEventIdContact, t0.objIdExcursion, t0.objOriginIdContact, t0.objPreparationIdContact, t0.objIdProject, t0.objSiteIdSite, t0.objdetIdTaxon
FROM tObjects t0
WHERE t0.objdetIdTaxon IN (
SELECT t1.taxIdTaxon.t1.taxIdTaxon
FROM tTaxa t1
WHERE (t1.taxTaxonDisplay LIKE 'Test Taxon 1'
OR t1.taxSynIdTaxon IN (
SELECT t2.taxSynIdTaxon
FROM tTaxa t2
WHERE t2.taxTaxonDisplay LIKE 'Test Taxon 1')))
to take out the error:
SELECT t1.taxIdTaxon.t1.taxIdTaxon
which is complete crap. you cannot execute a function on an type of int!
resolving this error (BUG?) introduces a new construct (which still returns the same results)
SELECT t1.objIdObject, t1.objAdminCreated, t1.objAdminCreator, t1.objAdminEdited, t1.objAdminEditor, t1.objAdminImport1, t1.objAdminImport2, t1.objAddBool1, t1.objAddBool2, t1.objAddBool3, t1.objAddBool4, t1.objAddBool5, t1.objAddDateTime1, t1.objAddDateTime2, t1.objCommonComments, t1.objCommonDescription, t1.objCommonKeywords, t1.objCommonName, t1.objCommonPublished, t1.objCommonPublishedAs, t1.objCommonStatus, t1.objCommonType, t1.objCommonTypustype, t1.objDetAccuracy, t1.objDetCf, t1.objDetComments, t1.objDetDate, t1.objDetMethod, t1.objDetResult, t1.objAddFloat1, t1.objAddFloat2, t1.objAddFloat3, t1.objAddFloat4, t1.objAddFloat5, t1.objEventAbundance, t1.objEventCollectionMethod, t1.objEventComments, t1.objEventMoreContacts, t1.objEventDateDay1, t1.objEventDate1, t1.objEventDateMonth1, t1.objEventDate2, t1.objEventDateUncertain, t1.objEventDateYear1, t1.objEventEcosystem, t1.objEventHabitat, t1.objEventNumber, t1.objEventPermission, t1.objEventSubstratum, t1.objEventTime1, t1.objEventTime2, t1.objEventWeekNumber, t1.objFlora, t1.objGuidObject, t1.objIOComments, t1.objIODeAccessed, t1.objAddInt1, t1.objAddInt2, t1.objAddInt3, t1.objAddInt4, t1.objAddInt5, t1.objStorageForeignNumber, t1.objStorageNumber, t1.objStorageNumberInCollection, t1.objStorageNumberOld, t1.objStorageNumberPrefix, t1.objAddLkp1, t1.objAddLkp10, t1.objAddLkp2, t1.objAddLkp3, t1.objAddLkp4, t1.objAddLkp5, t1.objAddLkp6, t1.objAddLkp7, t1.objAddLkp8, t1.objAddLkp9, t1.objAddLkpCs1, t1.objAddLkpCs10, t1.objAddLkpCs11, t1.objAddLkpCs12, t1.objAddLkpCs13, t1.objAddLkpCs14, t1.objAddLkpCs15, t1.objAddLkpCs2, t1.objAddLkpCs3, t1.objAddLkpCs4, t1.objAddLkpCs5, t1.objAddLkpCs6, t1.objAddLkpCs7, t1.objAddLkpCs8, t1.objAddLkpCs9, t1.objOriginAccessionDate, t1.objOriginAccessionNumber, t1.objOriginComments, t1.objOriginMoreContacts, t1.objOriginSource, t1.objOriginType, t1.objPreparationComments, t1.objPreparationDate, t1.objPreparationType, t1.objPropAdults, t1.objPropAge, t1.objPropAgeUnit, t1.objPropEggs, t1.objPropFemale, t1.objPropHeight, t1.objPropHeightUnit, t1.objPropJuveniles, t1.objPropLarvae, t1.objPropLength, t1.objPropLengthUnit, t1.objPropMale, t1.objPropObservation, t1.objPropObservationComments, t1.objPropPupae, t1.objPropSex, t1.objPropStadium, t1.objPropWeight, t1.objPropWeightUnit, t1.objPropWidth, t1.objPropWidthUnit, t1.objSiteComments, t1.objStorageComments, t1.objStorageContainerNumber, t1.objStorageContainerPieces, t1.objStorageContainerType, t1.objStorageLevel1, t1.objStorageLevel2, t1.objStorageLevel3, t1.objStorageLevel4, t1.objStorageLevel5, t1.objStorageNumberInContainer, t1.objstoragePieces, t1.objStorageValue, t1.objStorageValueUnit, t1.objAddText1, t1.objAddText10, t1.objAddText2, t1.objAddText3, t1.objAddText4, t1.objAddText5, t1.objAddText6, t1.objAddText7, t1.objAddText8, t1.objAddText9, t1.objIdCollection, t1.objCommonIdReference, t1.objDetIdContact, t1.objDetIdReference, t1.objEventIdContact, t1.objIdExcursion, t1.objOriginIdContact, t1.objPreparationIdContact, t1.objIdProject, t1.objSiteIdSite, t1.objdetIdTaxon
FROM tTaxa t0, tObjects t1
WHERE (
t0.taxIdTaxon IN (
SELECT t2.taxIdTaxon
FROM tTaxa t2
WHERE (t2.taxTaxonDisplay LIKE 'Test Taxon 1'
OR t2.taxSynIdTaxon IN (
SELECT t3.taxSynIdTaxon
FROM tTaxa t3
WHERE t3.taxTaxonDisplay LIKE 'Test Taxon 1'
)
)
) AND (t0.taxIdTaxon = t1.objdetIdTaxon)
)
this seems strange to me, but it is working - and it is faster than my alternative query, which includes a inner join
NOTE: Eclipselink does ignore the JoinType. regardless what you pass it takes an left outer join. (documentation says something else!)
finally i provide the two examples for join and joinless
private static Predicate addSynonymsWithJoins(Root<BioObjectImpl> r, CriteriaBuilder b, CriteriaQuery cq,
Attribute attr, Path path, Object value) {
Join taxJoin = r.join(BioObjectEnum.taxon.name(), JoinType.INNER);
Path<Object> taxValidSynonymId = taxJoin.get(TaxonEnum.validSynonymId.name());
Subquery<TaxonImpl> innerSubquery = cq.subquery(TaxonImpl.class);
Root fromSubTax = innerSubquery.from(TaxonImpl.class);
innerSubquery.select(fromSubTax.<Integer> get(TaxonEnum.id.name()));
Predicate dynamic1 = cb.like(fromSubTax.get(TaxonEnum.name.name()),
NAME_LIKE);
innerSubquery.where(dynamic1);
Predicate dynamic2 = resolveComparator(b, attr, taxJoin.get(attr.getPropertyName()), attr.getValue());//
Predicate p = b.or(taxValidSynonymId.in(innerSubquery), dynamic2);
return p;
}
private static Predicate addSynonymsWithoutJoins(Root<BioObjectImpl> r, CriteriaBuilder b, CriteriaQuery cq,
Attribute attr, Path path, Object value) {
cq.select(r);
Path<Integer> objTaxonId = r.<Integer> get(BioObjectEnum.taxon.name()).get(TaxonEnum.id.name());
Subquery<Integer> t2 = cq.subquery(Integer.class);
Root<TaxonImpl> t2fromTaxon = t2.from(TaxonImpl.class);
Path<Integer> t2taxId = t2fromTaxon.<Integer> get(TaxonEnum.validSynonymId.name());
t2.select(t2taxId);
Predicate t2dynamicWhere = resolveComparator(b, attr, t2fromTaxon.get(attr.getPropertyName()), attr.getValue());
t2.where(t2dynamicWhere);
Subquery<Integer> t1 = cq.subquery(Integer.class);
Root<TaxonImpl> t1fromTaxon = t1.from(TaxonImpl.class);
Predicate t1dynamicWhere = b.like(fromSubTax.get(TaxonEnum.name.name()),
NAME_LIKE);
Path<Integer> t1Select = t1fromTaxon.<Integer> get(TaxonEnum.id.name());
t1.select(t1Select);
Path<Integer> t1TaxSynonymId = t1fromTaxon.<Integer> get(TaxonEnum.validSynonymId.name());
t1dynamicWhere = b.or(t1dynamicWhere, t1TaxSynonymId.in(t2));
t1.where(t1dynamicWhere);
Predicate where = objTaxonId.in(t1);
return where;
}

Envers throws RuntimeException in ValidityAuditStrategy when no audit records (table partitioning)

In envers (persistence.xml), I enabled the strategy for table partitioning, according to the development guide: http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch15.html#envers-partitioning
The class: ValidityAuditStrategy, throws RuntimeException when there is no audit record. This exception occurs when the Envers try to update an audit record with a date of final revision (revend_tstmp), but this audit record does not exist.
The database of my application receives data load from external applications and is not possible change these external applications to include their audit records.
I have no chance to handle this exception (I don't know how).
In method ValidityAuditStrategy#updateLastRevision:
if (l.size() == 1) {
//... doStuff - OK
} else {
throw new RuntimeException("Cannot find previous revision for entity " + auditedEntityName + " and id " + id);
}
In method ValidityAuditStrategy#perform:
if ( rowCount != 1 )
throw new RuntimeException("Cannot update previous revision for entity " + auditedEntityName + " and id " + id);
A similar issue occurred in this link: https://developer.jboss.org/thread/160195?tstart=0 but had no solution.
It's possible apply a workaround?
I use hibernate-envers-4.1.3-Final version.
Log:
2015-07-17 10:23:28,653 DEBUG [-] [org.hibernate.SQL] (http-/0.0.0.0:8080-5) update MY_ENTITY_AUD set ID_REV_FINAL=?, DATE_HOUR_REV_FINAL=? where ID_ENTITY=? and ID_REV <> ? and ID_REV_FINAL is null
2015-07-17 10:23:28,677 TRACE [-] [org.hibernate.type.descriptor.sql.BasicBinder] (http-/0.0.0.0:8080-5) binding parameter [1] as [INTEGER] - 422
2015-07-17 10:23:28,677 TRACE [-] [org.hibernate.type.descriptor.sql.BasicBinder] (http-/0.0.0.0:8080-5) binding parameter [2] as [TIMESTAMP] - Thu Jul 17 10:23:28 BRT 2015
2015-07-17 10:23:28,677 TRACE [-] [org.hibernate.type.descriptor.sql.BasicBinder] (http-/0.0.0.0:8080-5) binding parameter [3] as [INTEGER] - 12345
2015-07-17 10:23:28,678 TRACE [-] [org.hibernate.type.descriptor.sql.BasicBinder] (http-/0.0.0.0:8080-5) binding parameter [4] as [INTEGER] - 422
2015-07-17 10:23:28,803 ERROR [-] [org.hibernate.AssertionFailure] (http-/0.0.0.0:8080-5) HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): java.lang.RuntimeException: Cannot update previous revision for entity my.package.MyEntity_AUD and id 12345
2015-07-17 10:23:28,841 WARN [-] [com.arjuna.ats.arjuna] (http-/0.0.0.0:8080-5) ARJUNA012125: TwoPhaseCoordinator.beforeCompletion - failed for SynchronizationImple< 0:ffffac1c045d:-3a5600e4:55a7c120:131, org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization#5619c5a3 >: org.hibernate.AssertionFailure: Unable to perform beforeTransactionCompletion callback
at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:754) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:338) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:490) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:114) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:53) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:76)
at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:273)
at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:93)
at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162)
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1189)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126)
at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.endTransaction(CMTTxInterceptor.java:92) [jboss-as-ejb3-7.4.0.Final-redhat-19.jar:7.4.0.Final-redhat-19]
...
Caused by: java.lang.RuntimeException: Cannot update previous revision for entity entity my.package.MyEntity_AUD and id 12345
at org.hibernate.envers.strategy.ValidityAuditStrategy.perform(ValidityAuditStrategy.java:210) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at org.hibernate.envers.synchronization.work.AbstractAuditWorkUnit.perform(AbstractAuditWorkUnit.java:76) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at org.hibernate.envers.synchronization.AuditProcess.executeInSession(AuditProcess.java:116) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at org.hibernate.envers.synchronization.AuditProcess.doBeforeTransactionCompletion(AuditProcess.java:155) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at org.hibernate.envers.synchronization.AuditProcessManager$1.doBeforeTransactionCompletion(AuditProcessManager.java:62) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:748) [hibernate-core-4.1.3-Final.jar:4.1.3-Final]
... 90 more
In persistence.xml existing a property to specify a custom AuditStrategey: org.hibernate.envers.audit_strategy.
Change,
From:
<property name="org.hibernate.envers.audit_strategy" value="org.hibernate.envers.strategy.ValidityAuditStrategy"/>
To:
<property name="org.hibernate.envers.audit_strategy" value="com.app.your.pack.YourCustomValidityAuditStrategy"/>
So now you can extend ValidityAuditStrategy and override perform() to not throw a RuntimeException when there is no previous revision for the entity, like this:
public class YourCustomValidityAuditStrategy extends ValidityAuditStrategy {
private final Log logger = LogFactory.getLog(getClass());
#Override
public void perform(Session session, String entityName, AuditConfiguration auditCfg, Serializable id, Object data, Object revision) {
try {
super.perform(session, entityName, auditCfg, id, data, revision);
} catch (RuntimeException re) {
if (logger.isDebugEnabled()) {
logger.debug("IGNORE RuntimeException: Cannot update previous revision for entity.", re);
}
}
}
Overriding just the perform method and catching the RuntimeException won't help you as the code throwing the RuntimeException is enclosed in an anonymous class of type BeforeTransactionCompletionProcess and is executed later.
The ValidityAuditStrategy is not very flexible so the only solution i see is ugly but should work : you must copy the entire ValidityAuditStrategy code in a custom class and catch the RuntimeException in the BeforeTransactionCompletionProcess anonymous class. Then specify your custom class in the persistence.xml :
<property name="org.hibernate.envers.audit_strategy "value="com.app.xxx.CustomValidityAuditStrategy"/>
The perform() method should look like the following :
#Override
public void perform(
final Session session,
final String entityName,
final EnversService enversService,
final Serializable id,
final Object data,
final Object revision) {
final AuditEntitiesConfiguration audEntitiesCfg = enversService.getAuditEntitiesConfiguration();
final String auditedEntityName = audEntitiesCfg.getAuditEntityName( entityName );
final String revisionInfoEntityName = enversService.getAuditEntitiesConfiguration().getRevisionInfoEntityName();
// Save the audit data
session.save( auditedEntityName, data );
// Update the end date of the previous row.
//
// When application reuses identifiers of previously removed entities:
// The UPDATE statement will no-op if an entity with a given identifier has been
// inserted for the first time. But in case a deleted primary key value was
// reused, this guarantees correct strategy behavior: exactly one row with
// null end date exists for each identifier.
final boolean reuseEntityIdentifier = enversService.getGlobalConfiguration().isAllowIdentifierReuse();
if ( reuseEntityIdentifier || getRevisionType( enversService, data ) != RevisionType.ADD ) {
// Register transaction completion process to guarantee execution of UPDATE statement after INSERT.
( (EventSource) session ).getActionQueue().registerProcess( new BeforeTransactionCompletionProcess() {
#Override
public void doBeforeTransactionCompletion(final SessionImplementor sessionImplementor) {
final Queryable productionEntityQueryable = getQueryable( entityName, sessionImplementor );
final Queryable rootProductionEntityQueryable = getQueryable(
productionEntityQueryable.getRootEntityName(), sessionImplementor
);
final Queryable auditedEntityQueryable = getQueryable( auditedEntityName, sessionImplementor );
final Queryable rootAuditedEntityQueryable = getQueryable(
auditedEntityQueryable.getRootEntityName(), sessionImplementor
);
final String updateTableName;
/*commented code*/
...
/*comment the following piece of code*/
/*if ( rowCount != 1 && ( !reuseEntityIdentifier || ( getRevisionType( enversService, data ) != RevisionType.ADD ) ) ) {
throw new RuntimeException(
"Cannot update previous revision for entity " + auditedEntityName + " and id " + id
);
}*/
}
});
}
sessionCacheCleaner.scheduleAuditDataRemoval( session, data );
}
As i said its ugly...
Setting org.hibernate.envers.allow_identifier_reuse: true helped in my scenario.
Not exactly answers the original question, but on the outside looks the same: Cannot update previous revision for entity my.package.MyEntity_AUD and id caa4ce8e.
I am using hibernate-envers-5.4.1 and for whatever reason (probably some buggy import in the past) suddenly faced the same error.
Direct query to the database select * from myentity_aud where id='caa4ce8e' resulted in:
rev revend revtype id ...
2121736 NULL 0 caa4ce8e ...
2121737 NULL 1 caa4ce8e ...
2121738 NULL 1 caa4ce8e ...
-- as seen, revend is NULL for all records.
The issue is: envers expects only one (the latest) to be NULL, all the rest must have the "overriding" rev to be set as revend.
So, to fix this particular case, it was enough to update to:
rev revend revtype id ...
2121736 2121737 0 caa4ce8e ...
2121737 2121738 1 caa4ce8e ...
2121738 NULL 1 caa4ce8e ...
and after that everything worked like a charm.
However, if you have millions of such records, you may want to write some script which will take care of them automatically.

Aerospike : Retrieve a set of keys from LDT Bin in one call

Suppose In my LDT(LargeMap) Bin I have following values,
key1, value1
key2, value2
key3, value3
key4, value4
. .
key50, value50
Now, I get my required data using following snippet :
Map<?, ?> myFinalRecord = new HashMap<?, ?>();
// First call to client to get the largeMap associated with the bin
LargeMap largeMap = myDemoClient.getLargeMap(myPolicy, myKey, myLDTBinName, null);
for (String myLDTKey : myRequiredKeysFromLDTBin) {
try {
// Here each get call results in one call to aerospike
myFinalRecord.putAll(largeMap.get(Value.get(myLDTKey)));
} catch (Exception e) {
log.warn("Key does not exist in LDT Bin");
}
}
The problem is here if myRequiredKeysFromLDTBin contains say 20 keys. Then largeMap.get(Value.get(myLDTKey)) will make 20 calls to aerospike.
Thus if I go by retrieval time of 1 ms per transaction , here my one call to retrieve 20 ids from a record will result in 20 calls to aerospike. This will increase my response time to approx. 20 ms !
So is there any way where I can just pass a set of ids to be retrieved from a LDT Bin and it takes only one call to do so ?
There is no direct API to do multi-get. A way of doing this would be call lmap API directly from server multiple time through UDF.
Example 'mymap.lua'
local lmap = require('ldt/lib_lmap');
function getmany(rec, binname, keys)
local resultmap = map()
local keycount = #keys
for i = 1,keycount,1 do
local rc = lmap.exists(rec, binname, keys[i])
if (rc == 1) then
resultmap[keys[i]] = lmap.get(rec, binname, keys[i]);
else
resultmap[keys[i]] = nil;
end
end
return resultmap;
end
Register this lua file
aql> register module 'mymap.lua'
OK, 1 module added.
aql> execute lmap.put('bin', 'c', 'd') on test.demo where PK='1'
+-----+
| put |
+-----+
| 0 |
+-----+
1 row in set (0.000 secs)
aql> execute lmap.put('bin', 'b', 'c') on test.demo where PK='1'
+-----+
| put |
+-----+
| 0 |
+-----+
1 row in set (0.001 secs)
aql> execute mymap.getmany('bin', 'JSON["b","a"]') on test.demo where PK='1'
+--------------------------+
| getmany |
+--------------------------+
| {"a":NIL, "b":{"b":"c"}} |
+--------------------------+
1 row in set (0.000 secs)
aql> execute mymap.getmany('bin', 'JSON["b","c"]') on test.demo where PK='1'
+--------------------------------+
| getmany |
+--------------------------------+
| {"b":{"b":"c"}, "c":{"c":"d"}} |
+--------------------------------+
1 row in set (0.000 secs)
Java Code to invoke this would be
try {
resultmap = myClient.execute(myPolicy, myKey, 'mymap', 'getmany', Value.get(myLDTBinName), Value.getAsList(myRequiredKeysFromLDTBin)
} catch (Exception e) {
log.warn("One of the key does not exist in LDT bin");
}
Value will be set if key exists and it would return NIL if it does not.

JPA 2 Constructor Expression cannot use constructor

I use OpenJPA 2.1.1 on WebSphere Application Server 8.
I want to create object from SELECT query using constructor expression:
String queryString = "SELECT NEW mypackage.StatisticDataObject(c.source, "
+ "SUM(CASE WHEN (c.validTo <= CURRENT_TIMESTAMP AND c.expireNoProblem LIKE 'N') THEN 1 ELSE 0 END ), "
+ "SUM(CASE WHEN (c.validTo <= CURRENT_TIMESTAMP AND c.expireNoProblem LIKE 'Y') THEN 1 ELSE 0 END ), "
+ "SUM(CASE WHEN (c.validTo > :timestamp30 ) THEN 1 ELSE 0 END ), "
+ "SUM(CASE WHEN (c.validTo > :timestamp10 AND c.validTo <= :timestamp30 ) THEN 1 ELSE 0 END ), "
+ "SUM(CASE WHEN (c.validTo > CURRENT_TIMESTAMP AND c.validTo <= :timestamp10 ) THEN 1 ELSE 0 END ) )"
+ "FROM MYTABLE c GROUP BY c.source";
TypedQuery<StatisticDataObject> q = em.createQuery(queryString,
StatisticDataObject.class);
q.setParameter("timestamp30", getTimestampIn(30));
q.setParameter("timestamp10", getTimestampIn(10));
Constructor:
public StatisticDataObject(String name, Integer expired,
Integer expiredButOK, Integer expireIn10Days, Integer expireIn30Days,
Integer expireGT30Days) {
this.name = name;
this.expired = expired;
this.expiredButOK = expiredButOK;
this.expireIn10Days = expireIn10Days;
this.expireIn30Days = expireIn30Days;
this.expireGT30Days = expireGT30Days;
}
But I get the following exception:
Caused by: <openjpa-2.1.1-SNAPSHOT-r422266:1141200 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: Query "SELECT NEW mypackage StatisticDataObject(c.source, ... at org.apache.openjpa.kernel.QueryImpl.execute(QueryImpl.java:872)
at org.apache.openjpa.kernel.QueryImpl.execute(QueryImpl.java:794)
at org.apache.openjpa.kernel.DelegatingQuery.execute(DelegatingQuery.java:542)
at org.apache.openjpa.persistence.QueryImpl.execute(QueryImpl.java:315)
at org.apache.openjpa.persistence.QueryImpl.getResultList(QueryImpl.java:331)
...
Caused by: java.lang.RuntimeException: Es wurde kein Konstruktor für "class mypackage.StatisticDataObject" mit den Argumenttypen "[class java.lang.String, class java.lang.String, class java.lang.String, class java.lang.String, class java.lang.String, class java.lang.String]" gefunden, um die Daten einzutragen.
// ENGLISH Translation: Caused by: java.lang.RuntimeException: There is no constructor "class mypackage.StatisticDataObject" with argument type "[class java.lang.String, class java.lang.String, class java.lang.String, class java.lang.String, class java.lang.String, class java.lang.String]".
at org.apache.openjpa.kernel.FillStrategy$NewInstance.findConstructor(FillStrategy.java:139)
at org.apache.openjpa.kernel.FillStrategy$NewInstance.fill(FillStrategy.java:144)
at org.apache.openjpa.kernel.ResultShape.pack(ResultShape.java:362)
at org.apache.openjpa.kernel.ResultShapePacker.pack(ResultShapePacker.java:48)
at org.apache.openjpa.kernel.QueryImpl$PackingResultObjectProvider.getResultObject(QueryImpl.java:2082)
at org.apache.openjpa.lib.rop.EagerResultList.<init>(EagerResultList.java:36)
at org.apache.openjpa.kernel.QueryImpl.toResult(QueryImpl.java:1251)
at org.apache.openjpa.kernel.QueryImpl.execute(QueryImpl.java:1007)
at org.apache.openjpa.kernel.QueryImpl.execute(QueryImpl.java:863)
... 84 more
If I run the query without NEW mypackage.StatisticDataObject() it works by using Object[]. Also the class of object[1-5] (.getClass()) is Integer.
So why does JPA return a String from SUM() instead of Integer when using the constructor expression?
In your constructor take in count that the state of the field type in which you will apply SUM need to be numeric, and the result type must correspond on the field type. For example, if a Double field is summed , the result will be returned as Double,. If a Long field type is summed, the response will be returned as a Long.
It is the root cause of the problem, SUM not return a Integer type.

Categories

Resources