spring boot + spring data jpa #query invaild - spring-data-jpa

spring boot 2.0
spring data jpa 2
#Modifying
#Query(name = "delete from User a where age=?1 and username=?1")
void deleteByAge(int i);
#Query is invalid. Did not execute the JPQL I wrote
question 2:
#Modifying
#Query(name = "delete from User a where age=?1 and username=?1")
void dByAge(int i);
spring boot startup exception:
Invocation of init method failed; nested exception is
java.lang.IllegalArgumentException: Failed to create query for method
public abstract void
com.unuobi.testboot.repository.UserRepository.dByAge(int)! No property
dByAge found for type User!

✔ #Query(value="select ....")
✖ #Query(name="select ....")
must be value ,

Use this as JPQL Query required age and username so both has to pass in your method , whatever type username is and they should be always in sequence they used in #Query:
#Modifying
#Query("delete from User a where age=?1 and username=?1")
void deleteByAge(int age, String username);
Or named query can be:
void deleteByAgeAndUsername(int age, String username);
make sure you have age and username in User entity.

Related

Spring JPA repository casting error when using JPQL

I have a PagingAndSorting JPA repository declared. I am using the #Query annotation.
I am getting an exception when I call the get() method on an Optional object from the findById(id) method of the repository.
The weird thing is it only happens when I use JPQL.
The code works if my query is native:
#Override
public BatchDto findById(String id) {
Optional<Batch> findResult = this.batchRepository.findById(id);
if (!findResult.isPresent()) return null;
Batch entity = findResult.get(); **<-------- Cast Exception Here**
BatchDto dto = this.mapper.toDto(entity, BatchDto.class);
List<BatchTransaction> transactions = entity.getTransactions();
dto.setTransactionDtos(mapper.toListDto(transactions, TransactionDto.class));
return dto;
}
Inspecting the findResult object with a breakpoint - I can see:
Optional[net.domain.data.batch#4b8bb6f]
when I have nativeQuery = true in the #Query annotation.
#Query(value = Sql.FindBatchById, nativeQuery = true)
Here is the query being used:
SELECT DISTINCT(B.batchNumber), COUNT(B.batchNumber) as TransactionCount FROM BATCH B WHERE B.batchReferenceNumber = :id GROUP BY B.batchNumber
However if I change it to JPQL and remove the nativeQuery=true attribute - the findResult is
Optional[[Ljava.lang.Object;#76e04327].
and I get a ClassCastException:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to net.domain.data.batch
So bottom line - this works when specify nativeQuery=true and fails when I try to use JPQL.
I would prefer not to specify nativeQuery as we will eventually port this db to Oracle.
First of all the query shown below doesn't return a single Batch instance. Since there are distinct and count aggregate functions, the query will return a List of aggregates.
To be able to read that statistics you can add appropriate method into the batchRepository. Something like this:
#Query("SELECT DISTINCT(B.batchNumber) as dist, COUNT(B.batchNumber) as cnt FROM BATCH B GROUP BY B.batchNumber")
List<Map<Long, Long>> findStatistics();
and then iterate through the list.
UPD
If the id parameter exactly guarantee that will return a single record, you can change a return type to a Map
#Query("SELECT DISTINCT(B.batchNumber) as dist, COUNT(B.batchNumber) as cnt FROM BATCH B WHERE B.batchReferenceNumber = :id GROUP BY B.batchNumber")
Map<Long, Long> findStatisticsById(#Param("id") Long id);

Executing JPQL query in PersistenceException catch clause re-throws PersistenceException

I am trying to achieve some error handling in my DAO in my JavaEE application, using JPA.
I have the situation where someone (a user) might try to enter duplicates into my DB. My plan is to attempt to persist my entity, eg. user("test#test.com", "Test", "password"). If this fails with a PersistenceException I am thinking that I can check for duplicate entries on the unique columns like username ("Test") and email ("test#test.com). If I find a duplicate I'd like to check which column it failed for and notify the user accordingly.
My attempt is as follows:
getEntityManager().persist(entity);
try {
getEntityManager().flush();
} catch (PersistenceException ex) {
List<T> duplicates = findDuplicate(entity);
if (duplicates.size() > 0) {
// Notify user
} else {
// Probably pass exception forwards
}
}
The Entity manager is injected into the class with:
#PersistenceContext(unitName = "RecruitmentPU")
protected EntityManager mEM;
The getEntityManager() simply return this member. The class it self is annotated as #Stateless.
To find a duplicate I basically just do this:
String column = myEntity.getUniqueColumn(); // returns the name of the column
Object uniqueValue = myEntity.getUniqueValue(); // returns the value of the unique column
Query query = getEntityManager().createQuery(
"SELECT e FROM TestEntity e WHERE " + column + " = :identifier",
TestEntity.class
);
query.setParameter("identifier", uniqueValue);
List<T> entries = null;
try {
entries = (List<T>) query.getResultList(); // Here the exception is re-thrown
} catch(Exception ex) {
System.out.println("Caught something... \n" + ex.getMessage());
}
The entity also has an ID column which is annotated #GeneratedValue(strategy = GenerationType.IDENTITY). There is also a #ManyToOne attribute that was removed when I simplified the code. When I test this I get the following output:
Info: Caught something...
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.3.qualifier): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'Test' for key 'username_UNIQUE'
Error Code: 1062
Call: INSERT INTO test.test (email, username, role) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(ID: 0 | email: test#test.com | username: Test | password: ********)
At the moment I'm letting the container handle transactions, but I'm having a hunch that I'm getting these problems because I'm trying to query the database before the first transaction is finished (or something similar).
Is the flaw in the strategy or the implementation? And what steps could I take to start solving this?
You don't want to continue any transaction after an exception has occurred.
I suggest you switch the order of operations, like so:
query DB for records with unique keys that are equal to the unique key of the entity you would like to persist,
persist your entity.

Error: Parameter value did not match expected type

I am trying to retrieve a USER from my database using the ID in the WHERE clause. However I am receiving an error and my program is failing.
This is the error I'm receiving when I run my program:
ERROR [org.jboss.as.ejb3.invocation] (default task-19)
JBAS014134: EJB Invocation failed on component CustomerServiceBeanImpl
for method public abstract package.name.entity.ICustomer
package.name.bean.CustomerServiceBean.getCustomerById(long):
javax.ejb.EJBException: java.lang.IllegalArgumentException:
Parameter value [19533] did not match expected type [package.name.entity.User (n/a)]
Note: [19533] is a test value I used.
This is the method that is having the error in the CustomerServiceBeanImpl.java:
#Override
public Customer getCustomerById (final long id)
{
return Customer.getById (this.em, id);
}
This is the method that's being called by the component CustomerServiceBeanImpl:
public static Customer getById (final EntityManager em, final long id)
{
for (final Customer c : em.createNamedQuery ("Customer.getById", Customer.class)
.setParameter ("id", id).setMaxResults (1).getResultList ())
{
return c;
}
return null;
}
The name query is this:
#NamedQuery (name = "Customer.getById",
query = "SELECT o FROM gnf.Customer o WHERE o.user = :id")
In the Customer.java class itself the relevant column is this one:
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn (name = "user_id")
private User user;
Doing a quick check of my ERD the "id" column in my "customer" table has a data type of BIGINT. However I'm not sure if that matters. (PostgreSQL database by the way.)
How can I fix this error?
The WHERE clause in your named query seems to be the problem. The attribute user in your Customer.class is of type User and your query expects it to be a type compatible to long.
...
Parameter value [19533] did not match expected type [package.name.entity.User ...
So if you need more help on this it would be great to see the complete entities User and Customer.
It is happening because in your database the parameter will be a #oneToOne object and you have to call the id inside the Object so you have to give the query as following :
"select user from User user where user.customer.id=:param"

Datetime to Date (MySQL) in JPA createQuery error Exception [EclipseLink-8025]

I'am new in JAVA and JPA.
I use command
select * from asm.attendant where staff_no='0004' and node_code='KT1' and date(attendant_date) = '2013-05-13';
in MySQL it's work
but
when i use in JPA
public List<Attendant> listAttendantByStaffNoAndNodeCodeAndDateWs(String staffNo,String nodeCode,String attendantDateData) throws ParseException {
Date attendantDate = new SimpleDateFormat("yyyy-MM-dd").parse(attendantDateData);
Query result = em.createQuery("SELECT a FROM Attendant a WHERE a.staffNo = :staffNo AND a.nodeCode = :nodeCode AND DATE(a.attendantDate) = :attendantDate", Attendant.class);
result.setParameter("staffNo", staffNo);
result.setParameter("nodeCode", nodeCode);
result.setParameter("attendantDate", attendantDate);
return result.getResultList();
}
it's not work.
error
Caused by: Exception [EclipseLink-8025] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Syntax error parsing the query [SELECT a FROM Attendant a WHERE a.staffNo = :staffNo AND a.nodeCode = :nodeCode AND DATE(a.attendantDate) = :attendantDate], line 1, column 88: unexpected token [(].
Internal Exception: NoViableAltException(83#[()* loopback of 383:9: (d= DOT right= attribute )*])
DATE() is not valid JPQL. In JPA createQuery() takes JPQL not SQL. If you want SQL use createNativeQuery().
Or, in JPQL just mark the date as a parameter :date any set the parameter to your instance of java.sql.Date.

JPA createNativeQuery call procedure with non standart name

I'm trying to call an Oracle procedure using Persistence API:
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void removeProcess(User user, BigDecimal processId) {
EntityManager em = emb.createEntityManager(user);
em.createNativeQuery("{ call DOF.DF#DEL_PROCESS(?) }")
.setParameter(1, processId)
.executeUpdate();
em.close();
}
And I got the following exception:
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Invalid column index
Error Code: 17003
Call: { call DOF."DF?)" }
bind => [1 parameter bound]
If i copy DF#DEL_PROCESS to DF_DEL_PROCESS all works fine. How can I escape # in the procedure name??
'#' is the default parameter marker used internal in EclipseLink.
You can change this using the query hint, "eclipselink.jdbc.parameter-delimiter"
Also, if you inline the parameter instead of using setParameter() it should also work.