JPA Converter unnessecary call of "convertToDatabaseColumn" leads to ClassCastException - java

I have a Converter to convert the String stored in the DB to the corresponding Enum element:
#Converter(autoApply=true)
public class DokumentTypConverter implements AttributeConverter<DokumentTypSchluessel, String> {
#Override
public String convertToDatabaseColumn(DokumentTypSchluessel attribute) {
return attribute.getValue();
}
#Override
public DokumentTypSchluessel convertToEntityAttribute(String dbData) {
return DokumentTypSchluessel.from(dbData);
}
}
I have an Entity class which has a field of the Enum type:
#Entity
#Table(name="REF_DOKUMENTTYPEN")
public class DokumentTyp {
private DokumentTypSchluessel schluessel;
// snip
}
Now when I try to retrieve an instance of that entity from the db
String query = "SELECT d FROM DokumentTyp d WHERE d.schluessel = " + VORBEREITUNG;
entityManager.createQuery(query, DokumentTyp.class).getSingleResult();
I get the following error:
javax.persistence.PersistenceException: An exception occurred while calling convertToDatabaseColumn on converter class de.drvbund.pub.model.convert.DokumentTypConverter with value VORBEREITUNG
at org.eclipse.persistence.mappings.converters.ConverterClass.convertObjectValueToDataValue(ConverterClass.java:139)
at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.getFieldValue(AbstractDirectMapping.java:778)
at org.eclipse.persistence.internal.expressions.QueryKeyExpression.getFieldValue(QueryKeyExpression.java:420)
at org.eclipse.persistence.internal.expressions.ConstantExpression.printSQL(ConstantExpression.java:152)
at org.eclipse.persistence.expressions.ExpressionOperator.printDuo(ExpressionOperator.java:2241)
at org.eclipse.persistence.internal.expressions.CompoundExpression.printSQL(CompoundExpression.java:286)
at org.eclipse.persistence.internal.expressions.RelationExpression.printSQL(RelationExpression.java:899)
at org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter.translateExpression(ExpressionSQLPrinter.java:325)
at org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter.printExpression(ExpressionSQLPrinter.java:129)
at org.eclipse.persistence.internal.expressions.SQLSelectStatement.printSQL(SQLSelectStatement.java:1755)
at org.eclipse.persistence.internal.databaseaccess.DatabasePlatform.printSQLSelectStatement(DatabasePlatform.java:3268)
at org.eclipse.persistence.platform.database.OraclePlatform.printSQLSelectStatement(OraclePlatform.java:967)
at org.eclipse.persistence.internal.expressions.SQLSelectStatement.buildCall(SQLSelectStatement.java:843)
at org.eclipse.persistence.internal.expressions.SQLSelectStatement.buildCall(SQLSelectStatement.java:854)
at org.eclipse.persistence.descriptors.ClassDescriptor.buildCallFromStatement(ClassDescriptor.java:815)
at org.eclipse.persistence.internal.queries.StatementQueryMechanism.setCallFromStatement(StatementQueryMechanism.java:390)
at org.eclipse.persistence.internal.queries.StatementQueryMechanism.prepareSelectAllRows(StatementQueryMechanism.java:315)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareSelectAllRows(ExpressionQueryMechanism.java:1723)
at org.eclipse.persistence.queries.ReadAllQuery.prepareSelectAllRows(ReadAllQuery.java:904)
at org.eclipse.persistence.queries.ReadAllQuery.prepare(ReadAllQuery.java:835)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:673)
... 151 more
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to de.drvbund.pub.enums.DokumentTypSchluessel
I guess the framework takes the toString()-representation of the Enum element from the query, assumes it is the entity attribute value, that needs to be converted to its corresponding dbValue and tries to call the convertToDatabaseColumn() method passing the String. I just don't know how to do this correctly.
How can I retrieve entities filtered by an Enum attribute value with JPA?

The problem was solved by the suggestion of Chris:
entityManager.createQuery("SELECT d FROM DokumentTyp d WHERE d.schluessel = ?1", DokumentTyp.class)
.setParameter(1, DokumentTypSchluessel.VORBEREITUNG)
.getSingleResult();

Related

Spring data jpa exception

While retrieving records from the DB table we are getting exceptions. I tried the same for another table it worked but for this table, it's not working. I am using spring-data-jpa
#Entity
#Table(name=USR.TABLE_NAME)
public class USR implements Serializable {
private static final long serialVersionUID = 1L;
public final static String TABLE_NAME = "usr_profile";
#Id
#Column (name="USR_NO")
private Integer usrNo;
#Column (name="USR_Address", length=20, nullable=true, unique=false)
private Integer usrAddress;
#Column (name="USR_COUNTRY", nullable=true, unique=false)
private String usrCountry;
other fields constructor, no-arg constructor, getter and setter removed for brevity
#Repository
public interface USRRepository extends JpaRepository<USR, Integer> {
#Query("SELECT o.usrNo, o.usrAddress, o.usrCountry, o.usrState, o.usrSt FROM USR o WHERE o.usrNo=?1")
USR findUsrRecordByUsrNo(Integer usrNo);
}
Here I have created a Controller class that has a get mapping
#GetMapping ("/CmpUsr")
public ResponseEntity<String> cmpLookup() {
USR us = usrRepository.findUsrRecordByUsrNo(12557);
return new ResponseEntity<>(us.toString(), HttpStatus.OK);
}
I am getting this exception
SEVERE: Servlet.service() for servlet [dispatcherServiet] in context with path[] threw exception (Request processing failed; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.Object[]] to type [com.newprof.userApp.domain.USR] for value (12557, 115 Minesota Yellow Rd, US, PH, 000991); nested exception is
org.springframework.core.comvert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.Integer to type [com.newprof.userApp.domain.USR]] with root cause
Try this code. I think the problem is the return object witch is not the same with select values.
#Repository
public interface USRRepository extends JpaRepository<USR, Integer> {
#Query("SELECT o FROM USR o WHERE o.usrNo=?1")
USR findUsrRecordByUsrNo(Integer usrNo);
}
Change this:
#Query("SELECT o.usrNo, o.usrAddress, o.usrCountry, o.usrState, o.usrSt FROM USR o WHERE o.usrNo=?1")
USR findUsrRecordByUsrNo(Integer usrNo);
}
with this:
#Query("SELECT new com.newprof.userApp.domain.USR(o.usrNo, o.usrAddress, o.usrCountry, o.usrState, o.usrSt) FROM USR o WHERE o.usrNo=?1")
USR findUsrRecordByUsrNo(Integer usrNo);
}
In exception it says: Failed to convert from type [java.lang.Object[]] to type [com.newprof.userApp.domain.USR].
Which basically means that your query statement returns an object array: java.lang.Object[].
So my plan is, you call your USR constructor straightaway in your query.
EDIT
FOUND THE ANSWER.
So you added this into your query:
new com.newprof.userApp.domain.USR(o.usrNo, o.usrAddress, o.usrCountry, o.usrState, o.usrSt)
And it now says to you: Cannot resolve constructor com.newprof.userApp.domain.USR(). IT IS RUGHT. Because there is no such constructor. You need to add it to your entity.
You need to add something like this:
public USR (Integer usrNo, Integer usrAddress, String usrCountry, String usrState, String usrSt) {
this.usrNo = usrNo;
this.usrAddress = usrAddress;
this.usrCountry = usrCountry;
this.usrState = usrState;
this.usrSt = isrSt;
}
This brand new Constructor will be called in statement new com.newprof.userApp.domain.USR(o.usrNo, o.usrAddress, o.usrCountry, o.usrState, o.usrSt) :D

Java - JPA-Specification: how to create criteria/specification on a field that belongs to a nested object?

I am using jdk 1.8 , hibernate and jpa in my project. And using specification/criteria to build my search query.
I have a class A ( an hibernate entity) which has class B as an attribute. So, roughly, it looks like :
#Entity
class A {
Long id;
String comment;
#OneToOne
B b;
}
and...
#Entity
class B {
Long id;
String type;
}
My repository class looks like (roughly):
public interface ARepository extends PagingAndSortingRepository<A, Integer>,
JpaSpecificationExecutor<A> {
}
Most of the simple JPA queries are working as expected. Even the specification/criteria based directly on Class A is working. However, I need to create a dynamic query and that should be executed under "findAll" method of PagingAndSortingRepository class. This query should be equivalent to
select * from A a left join B b on a.b_id = b.id
where b.type='final' and a.comment='blah';
I created a similar logic as above in a specification like :
public Specification<A> getSpecification() {
return (itemRoot, query, criteriaBuilder) -> {
.........
List<Predicate> partialQueries = new ArrayList<>();
partialQueries.add(criteriaBuilder.equal(itemRoot.get("b.type"), "final"));
partialQueries.add(criteriaBuilder.equal(itemRoot.get("comment"), "blah"));
//Other queries to be added...
return criteriaBuilder.and(partialQueries.toArray(new Predicate[0]));
};
}
And getting error :
Unable to locate Attribute with the the given name [b.type] on this ManagedType [com.something.domain.A]
Any insight on how to create criteria/specification on a field that belongs to a nested object?
If you want to filter nested object. You can write
itemRoot.get("NestedTableName").get("nestedfieldname")
In your case - itemRoot.get("B").get("type")
itemRoot.get("Name of the nested object field in the root class").get("nestedfieldname");
Example:
cb.equal(root.get("b").get("type"),value)
In your case - itemRoot.get("b").get("type");

Hibernate fetching deleted java enum types from db

I have an Enum class which has some values.
We've decided to remove one of these values and its all implementation from the code.
We dont want to delete any records from DB.
My Enum class is something like this:
public enum CourseType {
VIDEO("CourseType.VIDEO"),
PDF("CourseType.PDF"),
QUIZ("CourseType.QUIZ"),
SURVEY("CourseType.SURVEY"),
POWERPOINT("CourseType.POWERPOINT") //*this one will be removed*
...
}
My Course Entity:
#Entity
#Table(name = "CRS")
public class Course {
#Column(name = "COURSE_TYPE")
#Enumerated(EnumType.STRING)
private CourseType courseType;
#Column(name = "AUTHOR")
private String author;
....
#Override
public CourseType getCourseType() {
return courseType;
}
#Override
public void setCourseType(CourseType courseType) {
this.courseType = courseType;
}
....
}
After I removed the Powerpoint type from the Java Class and tried to fetch some values from the DB,
I get a mapping error for the removed type.
I have a code like this:
Course course = courseService.get(id);
If I gave a course id which its type is 'POWERPOINT' in the database,
the method gets the following error:
java.lang.IllegalArgumentException: Unknown name value [POWERPOINT]
for enum class [com.tst.enums.CourseType] at
org.hibernate.type.EnumType$NamedEnumValueMapper.fromName(EnumType.java:461)
at
org.hibernate.type.EnumType$NamedEnumValueMapper.getValue(EnumType.java:449)
at org.hibernate.type.EnumType.nullSafeGet(EnumType.java:107) at
org.hibernate.type.CustomType.nullSafeGet(CustomType.java:127) at
org.hibernate.type.AbstractType.hydrate(AbstractType.java:106) at
org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2912)
at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1673)
Is there any way when I try to retrieve a query result from DB,
hibernate will not fetch if that records' course_type column doesn't match with the any of the enum values in the code?
Do I have to use some kind of filter?
You can try use annotation #filter
#Filter(name = "myFilter", condition = "courseType <> 'CourseType.POWERPOINT'")
and enable it
session.enableFilter("myFilter")
If you can't use filters,
something like the following should work:
Add POWERPOINT back into the enum.
Add a deleted flag to the POWERPOINT enum value.
After the course list is loaded, remove courses that have a deleted courseType value.
New CourseType enum:
public enum CourseType
{
VIDEO("CourseType.VIDEO", false),
POWERPOINT("CourseType.POWERPOINT", true);
private boolean deletedFlag;
public CourseType(
existingParameter, // do whatever you are currently doing with this parameter
deletedFlagValue)
{
// code to handle existing parameter
deletedFlag = deletedFlagValue;
}

Parameter value [1] did not match expected type [java.lang.Boolean]

I'm getting the error Exception in thread "main" org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [1] did not match expected type [java.lang.Boolean]; nested exception is java.lang.IllegalArgumentException: Parameter value [1] did not match expected type [java.lang.Boolean].
I'm confused by this because it's coming from the service method shown below that is commented out. When I comment it out, the error is avoided. The active column is a TINYINT(1) that's either 1 or 0.
Entity:
#Entity
#NamedQueries({
#NamedQuery(name="Workflow.findByUUID", query="SELECT w FROM Workflow w WHERE w.uuid = :uuid"),
#NamedQuery(name="Workflow.findByActive", query="SELECT w FROM Workflow w WHERE w.active = :active ORDER BY id ASC")
})
My Repository:
#Repository
public interface WorkflowRepository extends JpaRepository<Workflow, Integer> {
List<Workflow> findByActive(#Param("active") Integer active);
}
My Service:
#Service
public class WorkflowService {
#Autowired
WorkflowRepository workflowRepository;
/**
* Get active workflows
*/
#Transactional(readOnly = true)
public List<Workflow> findActive() {
//return workflowRepository.findByActive(1);
return null;
}
When I uncomment
You seem to have mapped Workflow.active attribute as a Boolean. So you repository should be like:
#Repository
public interface WorkflowRepository extends JpaRepository<Workflow, Boolean> {
List<Workflow> findByActive(#Param("active") Boolean active);
}
And the call workflowRepository.findByActive(true) should behave how you want it to.
The fact is that HQL sometimes make a type difference between database and Java mapping, because typing isn't the same in these environments. So check your Entity's active type to make the appropriate Repository.

JPA enum query ERROR on playframework

I am working on project using java playframework 1.2.4 and I have a #Entity class. It is look like
#Entity
public class EmployeeType extends Model {
public static enum TYPE { HOURLY, DAILY, MONTHLY };
public static enum NATIONALITY { LOCAL, FOREIGN };
#Required
#Enumerated(EnumType.STRING)
public TYPE type;
#Required
#Enumerated(EnumType.STRING)
public NATIONALITY nationality;
}
And in my controller class I want to get list of EmployeeTypes using my 2 enum attributes.
Query looks like
Query query = JPA.em().createQuery("SELECT e FROM EmployeeType e where " +
"e.nationality = :nationality " +
"and e.type = :type");
query.setParameter("nationality", NATIONALITY.LOCAL);
query.setParameter("type", TYPE.HOURLY);
List<models.EmployeeType> employeeType = query.getResultList()
Gives this error: IllegalArgumentException occured : Parameter value [LOCAL] was not matching type [models.EmployeeType$NATIONALITY]
What should i do?
The error is possibly because of the fact that your enums are nested in your entity. You need to access it on entity name.
You can change your setParameter code to: -
query.setParameter("nationality", EmployeeType.NATIONALITY.LOCAL);
query.setParameter("type", EmployeeType.TYPE.HOURLY);

Categories

Resources