spring data jpa null parameter to native query - spring

exception details - Caused by : java.sql.SQLSyntaxErrorException: ORA-00932: inconsistent datatypes: exptected NUMBER got BINARY
public interface ExampleRepository extends JpaRepository {
public static final FINDSQL = " select a, b, c from exp_attr ea, dep_attr da where ea.id = da.id AND ea.sysId = :sysId";
#Query(value = FINDSQL , nativeQuery=true)
List<ProjectionInterface> getExampleDetails(#Param("sysId") Integer sysId);
}
public class RestApi {
#Autowired
private ExampleRepository exampleRepository;
public String testJpaRepo(){
Integer sysId = 123;
exampleRepository.getExampleDetails(sysId);
}
}
public interface ProjectionInterface {
Integer getA();
String getB();
String getC();
}
When I am passing sysId = 123, it works fine, but when sysId is null then it gives following exception
Caused by : java.sql.SQLSyntaxErrorException: ORA-00932: inconsistent datatypes: exptected NUMBER got BINARY

Related

Failed to convert value of type 'java.lang.String' to required type 'java.util.Date' on swagger

I want to reach a function in the database with the spring boot api and get the value it returns.
When we enter the parameters in swagger, it gives an error in the date part.
When I call the date parameters to the function in oracle as 01-apr-2021, there is no error, but I cannot send it this way from spring.
Oracle funtion code :
CREATE OR REPLACE PACKAGE BODY MET.Z_PKG_OEE_NEW
FUNCTION Z_OEE_A1AfterReworkRatio(V_plant_config_num_id IN number, p_start_date in date, p_stop_date in date) RETURN NUMBER IS
v_result NUMBER;
p_cur001 SYS_REFCURSOR;
BEGIN
Z_OEE_A1AfterReworkRatio_Detail(V_plant_config_num_id,p_start_date,p_stop_date,p_cur001, v_result);
RETURN round(v_result,4);
END Z_OEE_A1AfterReworkRatio;
end;
ooeController:
#RestController
#RequestMapping("/api/oeeReports")
#CrossOrigin
public class OeeController {
private OeeReportService oeeReportService;
#Autowired
public OeeController(OeeReportService oeeReportService) {
this.oeeReportService=oeeReportService;
}
#GetMapping("A1AfterReworkRatio")
BigDecimal A1AfterReworkRatio(#RequestParam int V_plant_config_num_id, #RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date p_start_date ,#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date p_stop_date) {
return this.oeeReportService.A1AfterReworkRatio( V_plant_config_num_id , p_start_date, p_stop_date);
}
}
oeeservice:
#Service
public class OeeReportManager implements OeeReportService {
private OeeDao oeeDao;
#Autowired
public OeeReportManager(OeeDao oeeDao) {
super();
this.oeeDao=oeeDao;
}
#Override
public BigDecimal A1AfterReworkRatio(int V_plant_config_num_id, Date p_start_date, Date p_stop_date) {
// TODO Auto-generated method stub
return this.oeeDao.A1AfterReworkRatio(V_plant_config_num_id, p_start_date, p_stop_date);
}
}
oeedao :
#Repository
public class OeeDao {
#Autowired
private EntityManager entitymanager;
public BigDecimal A1AfterReworkRatio(int V_plant_config_num_id,Date p_start_date,Date p_stop_date) {
BigDecimal commentCount = (BigDecimal) entitymanager
.createNativeQuery(
"SELECT Z_OEE_A1AfterReworkRatio(:V_plant_config_num_id:p_start_date:p_stop_date) FROM DUAL"
)
.setParameter("V_plant_config_num_id", V_plant_config_num_id).setParameter("p_start_date", p_start_date).setParameter("p_stop_date", p_stop_date)
.getSingleResult();
return commentCount;
}
}
swagger :
error :
{
"timestamp": "2021-08-26T07:00:23.487+00:00",
"status": 500,
"error": "Internal Server Error",
"trace": "org.springframework.dao.InvalidDataAccessApiUsageException: Could not locate named parameter [V_plant_config_num_id], expecting one of [V_plant_config_num_id:p_start_date:p_stop_date]; nested exception is java.lang.IllegalArgumentException: Could not locate named parameter [V_plant_config_num_id], expecting one of [V_plant_config_num_id:p_start_date:p_stop_date]\r\n\tat
How can solve this problem?
According to https://www.baeldung.com/spring-date-parameters
you can annotate your date parameters in OeeController (from: spring boot application {while creating beans error}) with #DateTimeFormat(iso = DateTimeFormat.ISO.DATE):
#GetMapping("A1AfterReworkRatio")
int A1AfterReworkRatio(#RequestParam int V_plant_config_num_id,
#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date p_start_date,
#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date p_stop_date) {
return this.oeeReportService.A1AfterReworkRatio( V_plant_config_num_id , p_start_date, p_stop_date);
}
The above article describes also other methods of achieving it if you read through it.

How to assign a numeric value to a specific field in the JPA method name?

Let's say I have an entity Products that has an int number field:
#Entity
#Table(name = "products")
public class Product {
#Id
#GeneratedValue(generator = "inc")
#GenericGenerator(name = "inc", strategy = "increment")
private int id;
private int number;
public int getId() {
return id;
}
void setId(int id) {
this.id = id;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
And there is a repository extending JpaRepository:
#Repository
interface SqlProductRepository extends JpaRepository<Product, Integer> {
Optional<List<Product>> findProductsByNumberIs0(); //compile error
}
Here is the error I get when I start the app:
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlProductRepository' defined in com.example.demo.adapter.SqlProductRepository defined in #EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.Optional com.example.demo.product.ProductRepository.findProductsByNumberIs0()! Reason: Failed to create query for method public abstract java.util.Optional com.example.demo.product.ProductRepository.findProductsByNumberIs0()! No property numberIs0 found for type Product!; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.Optional com.example.demo.product.ProductRepository.findProductsByNumberIs0()! No property numberIs0 found for type Product!
What can I do to get a list of products that has number field equal to 0 ?
This is not supported by query derivation, so you have to put in a little more work. The simplest variant is to provide a JPQL query in an annotation.
#Repository
interface SqlProductRepository extends JpaRepository<Product, Integer> {
#Query("select p from Product p where number = 0")
List<Product> someMethodName();
}
I removed the Optional wrapper since List already has a perfect representation of nothing: The empty list.
Try a slight variation on your method name and signature. You should be able to define your repository like this:
#Repository
interface SqlProductRepository extends JpaRepository<Product, Integer> {
List<Product> findProductsByNumber(int number);
}
And then if you want those products where number == 0, then you can call your repository's method, passing in 0 as the argument:
List<Product> products = sqlProductRepository.findProductsByNumber(0);
This also allows you the flexibility to call findProductsByNumber() with any arbitrary number for any other use case.
EDIT: the repository's method should return a List, but if no products matching your criteria are found, you will just end up with an empty list.

Spring Data: Query By Example and Converter

I have an entity:
import javax.persistence.Convert;
#Entity(name = "my_entity")
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Convert(converter = StringListConverter.class)
private List<String> emails;
// next fields omitted...
}
Converter which is used by emails field in entity (typical implementation like LocalDateTimeConverter):
import javax.persistence.Converter;
#Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
private static final String SPLIT_CHAR = ";";
#Override
public String convertToDatabaseColumn(List<String> stringList) {
if (CollectionUtils.isNotEmpty(stringList)) {
return String.join(SPLIT_CHAR, stringList);
} else {
return null;
}
}
#Override
public List<String> convertToEntityAttribute(String string) {
if (StringUtils.isNotBlank(string)) {
return Arrays.asList(string.split(SPLIT_CHAR));
} else {
return Collections.emptyList();
}
}
}
(I store emails separated by semicolons in one column. StringListConverter do that conversion.)
And Spring Data repository:
import org.springframework.data.domain.Example;
public interface MyRepository extends JpaRepository<MyEntity, Long> {
default List<MyEntity> findMatchingMyEntity(MyEntity myEntity) {
Example<MyEntity> example = Example.of(myEntity);
return findAll(example);
}
}
I use Query by Example mechanism from Spring Data. When I have fields without #Convert (like String name) it works. But when I have field with #Convert (AttributeConverter) like List<String> emails it causes InvalidDataAccessApiUsageException.
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [abc#company.com] did not match expected type [java.util.List (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [abc#company.com] did not match expected type [java.util.List (n/a)]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:374) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:257) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-5.2.1.RELEASE.jar:5.2.1.RELEASE]
...
Caused by: java.lang.IllegalArgumentException: Parameter value [abc#company.com] did not match expected type [java.util.List (n/a)]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:54) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:27) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
at org.hibernate.query.internal.QueryParameterBindingImpl.validate(QueryParameterBindingImpl.java:90) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
... 146 common frames omitted
(message is weird because I've tried search with that list: ["abc#company.com", "def#company.com"], but in message is only one email)
I've tried to implement transform in ExampleMatcher:
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
public interface MyRepository extends JpaRepository<MyEntity, Long> {
default List<MyEntity> findMatchingMyEntity(MyEntity myEntity) {
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("emails",
match -> match.transform(emailsOptional -> {
if (emailsOptional.isPresent()) {
List<String> emails = (List<String>) emailsOptional.get();
return Optional.ofNullable(new StringListConverter().convertToDatabaseColumn(emails));
}
return emailsOptional;
}));
Example<MyEntity> example = Example.of(myEntity, matcher);
return findAll(example);
}
}
But is causes InvalidDataAccessApiUsageException too, but with different message than previous one (there are two emails that I've set):
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [abc#company.com;def#company.com] did not match expected type [java.util.List (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [abc#company.com;def#company.com] did not match expected type [java.util.List (n/a)]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:374) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:257) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
Caused by: java.lang.IllegalArgumentException: Parameter value [abc#company.com;def#company.com] did not match expected type [java.util.List (n/a)]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:54) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:27) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
... 146 common frames omitted
It seems that for some reason Hibernate is trying to split the array of emails into multiple conditions just like in IN query in SQL using expandListValuedParameters method.
Note - with your solution doing query like findAllByEmailsIn(List<String> emailsList) also won't work.
Method expandListValuedParameters is deprecated since Hibernate 5.2, so it may contains some problems and for sure will be implemented differently in Hibernate 6.0.
I haven't found a fix for your problem, but there are some workarounds:
Wrap your List<String> emails in another class
Wrapper class:
public class EmailList {
private List<String> emails;
// getters, setters, constructors ommited
}
Updated model class:
import javax.persistence.Convert;
#Entity(name = "my_entity")
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Convert(converter = StringEmailListConverter.class)
private EmailList emailList;
// next fields omitted...
}
Updated converter class:
import javax.persistence.Converter;
#Converter
public class StringEmailListConverter implements AttributeConverter<EmailList, String> {
private static final String SPLIT_CHAR = ";";
#Override
public String convertToDatabaseColumn(EmailList emailList) {
if (emailList != null && CollectionUtils.isNotEmpty(emailList.getEmails())) {
return String.join(SPLIT_CHAR, emailList.getEmails());
} else {
return null;
}
}
#Override
public EmailList convertToEntityAttribute(String string) {
if (StringUtils.isNotBlank(string)) {
return new EmailList(Arrays.asList(string.split(SPLIT_CHAR)));
} else {
return new EmailList(Collections.emptyList());
}
}
}
And Spring Data repository will work fine with this code - no need for using transform:
import org.springframework.data.domain.Example;
public interface MyRepository extends JpaRepository<MyEntity, Long> {
default List<MyEntity> findMatchingMyEntity(MyEntity myEntity) {
Example<MyEntity> example = Example.of(myEntity);
return findAll(example);
}
}
Use String[] emails instead of List<String> emails
You need to change MyEntity and Converter respectively to use String[]. Of course using String[] sometimes is not an option because you specifically need a List.

I have an exception like invalidDataAccessApiUsageException

Here is the exception:
org.springframework.dao.InvalidDataAccessApiUsageException: Encountered array-valued parameter binding, but was expecting [java.lang.String (n/a)];
nested exception is java.lang.IllegalArgumentException: Encountered array-valued parameter binding, but was expecting [java.lang.String (n/a)]
Here is my Repository:
#Repository
public interface FirmRepository extends JpaRepository<Firm, Integer> {
Page<Firm> findAll(Pageable page);
List<Firm> findByParentFirm(Firm firm);
#Query(value = "FROM Firm WHERE name = :name")
Firm findByName(#Param("name") final String[] name);
}
Here is my Service:
/**
* Gets the Application type by name
*/
public Firm getFirmByName(final String[] name) throws ResourceNotFoundException, ForbiddenOperationException {
//LOGGER.debug("Getting the firm with name=[{}]", strings);
return firmRepository.findByName(name);
}
}
The following should work
#Query( "FROM Firm WHERE name in :names" )
List<Firm> findByName(#Param("names") List<String> firmNames);
You can convert the array to list as List<String> firmNames = Arrays.asList(names);

Converter doesn't work and not taken by criteria

i'm new here^^
i'm working with a db in which boolean are registered as VARCHAR(3) So i made a converter :
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter
public class BooleanToStringConverter implements
AttributeConverter<Boolean, String> {
#Override
public String convertToDatabaseColumn(Boolean value) {
if (value == null)
return "";
else if (value)
return "OUI";
else
return "NON";
}
#Override
public Boolean convertToEntityAttribute(String value) {
if (value == null)
return null;
else if (value.trim().equals("OUI"))
return true;
else if (value.trim().equals("NON"))
return false;
else if (value.trim().equals(""))
return null;
else
throw new IllegalStateException("Invalid boolean character: "
+ value);
}
}
i put my annotations #Convert:
public class Institution extends AbstractHREntity {
#Column(name = "IDOU00")
private String code;
#Column(table = "ZEXX", name = "LBOULG")
private String libelle;
#Column(table = "ZEXX", name = "LBOUSH")
private String abreviation;
#Column(table = "ZEYY", name = "LIBEXL")
private String libelleLong;
#Convert(converter = BooleanToStringConverter.class)
#Column(table = "ZEZZ", name = "ESTBUDGET", length = 3)
private Boolean isBudget;[/CODE]
But when i do a request with a criteria :
public List<Institution> findInstitutions(RechercheInstitutionData datas) throws DaoException{
List<Institution> resultList = new ArrayList<Institution>();
DetachedCriteria criteria = DetachedCriteria.forClass(Institution.class, "institution");
if(null!=datas.getInstitutionSearched())
{
if (StringUtils.isNotBlank(datas.getInstitutionSearched().getLibelleLong())){
criteria.add(Restrictions.like("institution.libelleLong", datas.getInstitutionSearched().getLibelleLong().toUpperCase(), MatchMode.START));
}
if (StringUtils.isNotBlank(datas.getInstitutionSearched().getAbreviation())){
criteria.add(Restrictions.like("institution.abreviation", datas.getInstitutionSearched().getAbreviation().toUpperCase(), MatchMode.START));
}
if (StringUtils.isNotBlank(datas.getInstitutionSearched().getLibelle())){
criteria.add(Restrictions.like("institution.libelle", datas.getInstitutionSearched().getLibelle(), MatchMode.START).ignoreCase());
}
if (StringUtils.isNotBlank(datas.getInstitutionSearched().getCode())){
criteria.add(Restrictions.like("institution.code", datas.getInstitutionSearched().getCode(), MatchMode.START));
}
criteria.addOrder(Order.asc("institution.code"));
}
resultList = find(criteria);
return resultList;
}
i had this error that occurred :
10:25:31,172 INFO [RechercheInstitution.beans.RechercheInstitutionBean] (http-localhost/127.0.0.1:8080-5) --> findInstitutions()
10:25:32,549 INFO [stdout] (http-localhost/127.0.0.1:8080-5) Hibernate: select this_.NUDOSS as NUDOSS1_35_0_, this_.IDOU00 as IDOU2_35_0_, this_1_.TBUDGE as TBUDGE1_39_0_, this_2_.LIBEXL as LIBEXL1_37_0_, this_3_.LBOUSH as LBOUSH1_36_0_, this_3_.LBOULG as LBOULG2_36_0_ from ZE00 this_ left outer join ZEWD this_1_ on this_.NUDOSS=this_1_.NUDOSS left outer join ZE04 this_2_ on this_.NUDOSS=this_2_.NUDOSS left outer join ZE01 this_3_ on this_.NUDOSS=this_3_.NUDOSS where this_3_.LBOUSH like ? order by this_.IDOU00 asc
10:25:33,310 WARN [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http-localhost/127.0.0.1:8080-5) SQL Error: -99999, SQLState: 07006
10:25:33,311 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http-localhost/127.0.0.1:8080-5) Data type mismatch. (For input string: "OUI")
10:25:33,313 ERROR [RechercheInstitution.ejb.institution.InstitutionMgrBean] (http-localhost/127.0.0.1:8080-5) InstitutionMgrBean.findInstitutions : common_HR.exception.DaoException: org.hibernate.exception.SQLGrammarException: could not execute query
10:25:33,315 INFO [RechercheInstitution.beans.RechercheInstitutionBean] (http-localhost/127.0.0.1:8080-5) <-- findInstitutions()
It looks like it doesn't use my converter, i tried breakpoints it doesn't go into my converter... In converter doc it says "all you have to do is put the #convert" but apparently no... Please help i'm really lost.

Resources