JPQL and Entities (java.lang.IllegalArgumentException) - glassfish

I am creating a web application that needs a table of notifications to display to various users. For some reason the JPQL query I've written is throwing a java.lang.IllegalArgumentException. My application already has a transaction table, structured and queried using an identical approach (afaik), which works perfectly.
I have been shuffling the code around, changing variable names and character cases for hours trying to get this to work but I'm still getting the exception every time. Does anyone have any idea where I'm going wrong?
My NotificationEntity is as follows:
#Table(name="notificationentity")
#NamedQuery(name="fetch_user_notifications", query="SELECT n FROM NotificationEntity n WHERE n.notificationrecipient=:username")
#Entity
public class NotificationEntity implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
String notificationSender;
#NotNull
String notificationrecipient;
... other fields + methods
}
The JPQL query is called from an EJB (NotificationStorageServiceBean) that implements an interface (NotificationStorageService) with the following method:
#Override
public synchronized List<NotificationEntity> getUserNotificationList(String username)
{
List notifications;
notifications = em.createNamedQuery("fetch_user_notifications").setParameter("notificationrecipient", username).getResultList();
return notifications;
}
And the EJB method is called from a CDI backing bean for my .xhtml UI, using the FacesContext's currently logged in user to provide the argument for these methods.
#EJB
NotificationStorageService notificationStore;
public List<NotificationEntity> getUserNotificationList()
{
return notificationStore.getUserNotificationList(this.currentUser);
}
The exact error I get is:
java.lang.IllegalArgumentException: You have attempted to set a parameter value using a name of notificationrecipient that does not exist in the query string SELECT n FROM NotificationEntity n WHERE n.notificationrecipient=:username.

The parameter name in a JPQL query starts with a colon. So just use
setParameter("username", username)

Related

Spring JPA - Define SQL function for usage in custom query

Let's assume we've an Spring JPA based repository
#Repository
public interface MyClassRepository extends CrudRepository<MyClass, Long> {
}
with the entity
#Entity
#Table(name = "classes")
public class MyClass {
private double latitude;
private double longitude;
// Getter + Setter
}
I tried to find all stored records within a given instance around a referenced object. For calculating the distance between first object's position and second object's position I thought about a custom #Query in combination with the Haversine Formula.
#Query("SELECT c FROM MyClass c WHERE :distance >= haversine(c, :origin)")
public List<Passenger> findAll(#Param("origin") MyClass origin, #Param("distance") double distance);
Is it possible to define the SQL function haversine() in an additional query, e.g. #Query("CREATE FUNCTION haversine(...) ..."), because I've to use this function in multiple custom queries and I try to prevent code repetition. Any ideas about this?
You can add your Sql function through for example db migrations, as usual, and extend org.hibernate.dialect (see how it made for Postgis for example) and don't forget specify the dialect for your data source
public class YourDialect extends PostgreSQL95Dialect {
public YourDialect() {
super();
registerFunction("haversine", new StandardSQLFunction("haversine", StandardBasicTypes.DOUBLE));
}
}
You must create a function in your DB. Functions are "static" elements - they must be present in DB on the time of the query execution.
Also, you have some errors in your method.
In the query you select from the table "MyClass", but your method returns "List".

How to construct Spring Data repository query two Parameters with IN and same list?

This is my Entity:
#Data
#Entity
#IdClass(EtlJobExecutionTriggersId.class)
#Table(name = "ETL_JOB_EXEC_TRIGGERS")
public class EtlJobExecutionTriggers {
#Id private Long jobExecIdUs;
#Id private Long jobExecIdDs;
private LocalDate cobDate;
}
And here is the Composite Primary Key Class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Embeddable
#EqualsAndHashCode
public class EtlJobExecutionTriggersId implements Serializable {
private Long jobExecIdUs;
private Long jobExecIdDs;
}
And here is my Spring Repo:
public interface EtlJobExecTriggersRepo extends JpaRepository<EtlJobExecutionTriggers, EtlJobExecutionTriggersId> {
String SQL_ = "select o from EtlJobExecutionTriggers o where o.jobExecIdDs in (:ids) or o.jobExecIdUs in (:ids) order by o.jobExecIdUs, o.jobExecIdDs";
#Query(EtlJobExecTriggersRepo.SQL_)
List<EtlJobExecutionTriggers> findAllByJobExecIdDsInAndJobExecIdUsInSQL(#Param("ids") List<Long> jobExecIdList);
}
The #Query works as expected, but I would like not to write any SQL and instead express the same Query using only Spring Data repository query.
I have tried the following (and other variants)
List<EtlJobExecutionTriggers> findAllByJobExecIdDsInAndJobExecIdUsInOrderByJobExecIdUsJobExecIdDs(List<Long> jobExecIdDsList)
But i keep getting errors when Booting. The above interface method yields the following exception for the OrderBy part:
org.springframework.data.mapping.PropertyReferenceException: No property jobExecIdDs found for type Long! Traversed path: EtlJobExecutionTriggers.jobExecIdUs.
So what am I doing wrong here? or is it not possible to express this particular query via Spring Data Repo query?
As I have written in my comment I fixed the Order by issue, but I am still unable to make it work with only one method parameter (List jobExecIdList)
When I make it with two (List jobExecIdDsList, List jobExecIdUsList)
Like this:
List<EtlJobExecutionTriggers> findAllByJobExecIdDsInAndJobExecIdUsInOrderByJobExecIdUsAscJobExecIdDsAsc(List<Long> jobExecIdDsList, List<Long> jobExecIdUsList);
it actually works but I can't get to work with only one list, as in the #Query("....") method
I think using your own custom id generator conflicts with Spring Data Repository query.
// You shoud have two parameters in your method as below.
List findAllByJobExecIdDsInAndJobExecIdUsInOrderByJobExecIdUsJobExecIdDs(List jobExecIdDsList,List jobExecIdUsList);

Native SQL select query using Spring JPA Data Annotation #Query, to cover non-empty, empty and null values at the same time

I have something like this in my repository class in a Spring project:
#Query(value = "SELECT * FROM accounts WHERE (first_name LIKE %:firstName% AND last_name LIKE %:lastName%)", nativeQuery = true)
public List<Account> searchByFirstnameAndLastname(#Param("firstName")String firstName,#Param("lastName")String lastName);
I want it to return everything if the parameters are not provided. Even the ones with null firstname/lastname. And it ignores the null values because of the wildcard used. Since null is different from ''.
I was thinking of an if-statement structure and building the query in runtime based on the provided parameters and then setting the value for the #Query annotation.
I tried generating the where clause and passing it as a parameter but it didn't work. I guess the way Spring Data JPA processes the value of this annotation caused it.
Any idea what is the best solution to this?
Have you tried containing keyword like below :
List<Account> findByFirstnameContainingAndLastNameContaining(String firstName,String lastName);
Docs: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
You cannot go far with #Query
For dynamic queries(with many optional filters), the way to go is using Criteria API or JPQL. I suggest the Criteria API as it is object oriented and suitable for dynamic queries.
I would suggest to use QueryDSL. It is mentioned in the docs JB Nizet already posted. There is is nice but quite old tutorial here.
With QueryDSL it is very convenient to create your queries dynamically and it is easier to understand than the JPA Criteria API.
The only difficulty in using QueryDSL is the need to automatically create the query objects from your entities but this can be automated by using maven.
There are two ways to handle your situation.
The hard way is using RepositoryFactoryBean as follow
create a custom RepositoryFactoryBean
public class DaoRepositoryFactoryBean, T, I extends Serializable>
extends JpaRepositoryFactoryBean
{
#Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager)
{
return new DaoRepositoryFactory(entityManager);
}
private static class DaoRepositoryFactory<E extends AbstractEntity, I extends Serializable> extends JpaRepositoryFactory
{
private EntityManager entityManager;
public DaoRepositoryFactory(EntityManager entityManager)
{
super(entityManager);
this.entityManager = entityManager;
}
#Override
protected Object getTargetRepository(RepositoryMetadata metadata)
{
return new DaoImpl<E>((Class<E>) metadata.getDomainType(), entityManager);
}
#Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata)
{
return Dao.class;
}
}
}
create Dao interface
#NoRepositoryBean
public interface Dao extends JpaRepository
{
List findByParamsOrAllWhenEmpty();
}
create your implementation
#Transactional(readOnly = true)
public class DaoImpl extends SimpleJpaRepository implements Dao
{
private EntityManager entityManager;
public DaoImpl(Class<E> domainClass, EntityManager em)
{
super(domainClass, em);
this.entityManager = em;
this.domainClass = domainClass;
}
List<E> findByParamsOrAllWhenEmpty()
{
//implement your custom query logic
//scan your domainClass methods for Query anotations and do the rest
}
}
introduce it to Spring Jpa Data
jpa:repositories
base-package=""
query-lookup-strategy="" factory-class="com.core.dao.DaoRepositoryFactoryBean"
The easy way using Custom Impl which in this case you can't use #Query annotation.
"coalesce" on MySQL or "IsNull" on SQL Server is my preferred solution. They return back the first non-null value of a list and you may use it as a trick to deal with an empty string just like a null:
#Query(value = "SELECT * FROM accounts WHERE (COALESCE(first_name,'') LIKE %:firstName% AND COALESCE(last_name,'') LIKE %:lastName%)", nativeQuery = true)
public List<Account> searchByFirstnameAndLastname(#Param("firstName")String firstName,#Param("lastName")String lastName);
Thanks to the questioner and the answerer :D at this page:
like '%' does not accept NULL value

When does NHibernate convert Lazy Collection back to a List

When exactly does NHibernate convert a collection that, while the Session is open is represented as a PersistentGenericBag, but later on is converted back to the IList that I defined in my source code?
I thought it was when either the transaction was committed, or the session was disconnected. But I'm writing a unit test and no matter how many things I close, flush, commit and disconnect, my object always has a PersistentGenericBag, not a List.
Thanks
Do not get me wrong. But do you really have mapping like this?
public class MyObject
{
public virtual List<string> MyColl { get; set; }
}
Because my experience says, that this will end up in exception like this:
NHibernate.PropertyAccessException: Invalid Cast (check your mapping for property type mismatches);
setter of MyLib.MyObject ---> System.InvalidCastException: Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericBag'1[System.String]' to type 'System.Collections.Generic.List1[System.String]`
What I would expect inside your mapping is IList<>. Because exactly this will allow the NHibernate to inject its own implementation of it. Namely: PersistentGenericBag
public class MyObject
{
public virtual IList<string> MyColl { get; set; }
}
And in that case, the answer is: never. The proxy of MyObject provides all functionality published by IList<> while still working with property of type PersistentGenericBag
And when is it really populated? when firstly accessed (via getter). Inner implementation of PersistentGenericBag will execute sql query and load data into internal IList<>. Of course session have to be still opened and connected to MyObject

NHibernate query by Transient instance results in "save the transient instance"-exception

I have some old code which is performing a query where a model can be transient. That is, a model with some fields populated from user input, which are then used as part of the query.
It worked under NH 2.1.x, but is failing under the latest version.
The exception raised is "object references an unsaved transient instance - save the transient instance before flushing". This happens when NH attempts to perform a query using a non-persisted object as part of the query.
A simplified version to illustrate the problem.
abstract class BaseModel
public virtual long Id { get; set; }
class Car : BaseModel
public virtual Engine Engine { get;set; }
class Engine : BaseModel
public virtual string Kind { get; set; }
public static IList<Car> GetByEngine(Engine eng) {
ICriteria c = Session.CreateCriteria<Car>();
c.Add(Expression.Eq("Engine", eng));
return c.List<Car>(); // <--- Error occurs here
}
And calling code is equivalent to this:
Engine obj = new Engine { Id = 42 }; // Transient instance
var x = GetByEngine(obj);
What I expected to happen (Which appears to be the behaviour of the old NHibernate version), is that the Engine passed is used only for getting the Id. That is, generating SQl like
select .... from Cars where Engine = 42
But with the new version NHibernate seems to check that the engine used in the Expression is actually persisted.
Is there a way to avoid having to load a persisted Engine before performing the query ?
yes using Session.Load() which returns the object if already in the session or a lazyLoadingProxy if not present.
public static IList<Car> GetByEngine(Engine eng) {
ICriteria c = Session.CreateCriteria<Car>();
c.Add(Expression.Eq("Engine", Session.Load<Engine>(eng.Id)));
return c.List<Car>();
}
You can use the Session.Load method, which exist for this kind of scenarios.
The Load method will return a Proxy to the Entity and won't hit the Data Base untill you access one of it's properties, (except the Primary key property which won't hit the DB at all).
Usage:
Engine obj = session.Load<Engine>(42);
var x = GetByEngine(obj);
check this article about Session.Get and Session.Load
I think you could do something like this:
public static IList<Car> GetByEngine(Engine eng) {
ICriteria c = Session.CreateCriteria<Car>().CreateCriteria("Engine");
c.Add(Expression.Eq("Id", eng.Id));
return c.List<Car>();
}
Anyway... how it's possible that a car with that engine exists if you haven't saved it yet?