Spring Find By Like Feature - java

I have a list of entities in my code (google.com, amazon.com, etc), and I would like to have my repository find an entity when I type feed it a string with a subdomain.
So basically, what repository function do I implement so I can run something like
public List<ValidDomainsEntity> findByCompanyDomainLike("www.google.com");
and have it return the entity with google.com in its
String companyDomain;
where the companyDomain = "google.com"
So in more layman's terms: My db contains URL's without the subdomain or prefix. What query do I use in spring's repository to find that URL when I put in a URL with a subdomain
Edit: Basically how I do the spring equivalent of
select * from valid_domains where 'www.amazon.com' LIKE CONCAT('%', companyDomain);

You can write it in several ways. First option is writing a native query.
#Query(value = "select * from valid_domains where 'www.amazon.com' LIKE CONCAT('%', :companyDomain", nativeQuery = true);
public List<ValidDomainsEntity> myQuery(#Param("companyDomain") String companyDomain);
Second option is writing a simple non native Query and use your Entiry class names and fields. For this you purpose you can use javax.persistence.EntityManager or #Query(value = "", nativeQuery = false). By default #Query is non native query, you dont have to write nativeQuery = true. I show also an EntityManager example.
#Autowired //Constructor dependency injection is more preferred instead of #Autowired
private EntityManager entityManager;
List<ValidDomain> query = entityManager.createQuery("SELECT new ValidDomain(d.id, d.domainName) from ValidDomain d where 'www.amazon.com' LIKE CONCAT('%', :variableName)", ValidDomain.class).getResultList();
Or you can also use EntityManager, entityManager.createNativeQuery() for creating native query.

Related

Execute dynamic query with Spring Data JPA

#Query(value = "SELECT * FROM H4 WHERE 1")
List getResult();
Instead of the query "SELECT * FROM H4 WHERE 1" I want to put a String variable containing query generated elsewhere.
#Query, like any other annotation, uses a compile time constant to define attributes. You can't define it's value in runtime unless you plan to hack the Spring Data JPA framework.
You should use either Specifications with criteria, Query by example or JPQL to define and execute your dynamic query.
Create constants and set the value as below.
#Query(value = ApplicationConstantClass.QUERY_STRING_CONSTANT)
List getResult();
or Use EntityManager or SessionFactory of hibernate to execute dynamically generated query.

How to create native queries in Spring?

I have a spring application that should connect to an existing database and just query an entity for existence based on some attributes.
I don't want to create a #Entity class for this. But I still want to use the spring managed EntityManager etc.
When using Spring, what is the best approach to just query a select for that entity? Using em.createNamedQuery(QUERY); with String QUERY = "SELECT count(*) from my_table where username =: username AND email := email)";?
Answers from #predrag-maric and #pL4Gu33 are both correct but if you use JPA in your project (for example, Hibernate) you might consider using #NamedNativeQuery annotation as well.
More about named native queries.
simple example of native query
#PersistenceContext
EntityManager em;
public String test(Integer id)
{
Query query = em.createNativeQuery("SELECT name FROM Accounts where id=?");
query.setParameter(1,id);
return query.getSingleResult();
}
You can use this method from entitymanager. http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#createNativeQuery%28java.lang.String%29
Use em.createNativeQuery(QUERY). Also, you'll have to use positional parameters (?1) instead of named parameters (:email), because only positional parameters are supported by JPA in native queries.

Spring Data - Why it's not possible to have paging with native query

Let's say we have an entity called MyEntity. It is possible to query pageable results using #Query and with named queries, e.g.
#Query(value = "select e from MyEntity e where e.enabled = true")
Page<MyEntity> findAllEnabled(Pageable pageable);
However, it is not possible to achieve the same with native query, so this
#Query(value = "select * from my_entity where enabled = true", nativeQuery = true)
Page<MyEntity> findAllEnabled(Pageable pageable);
won't work.
What are the reasons behind this? Is it possible to make Pageable working with native queries?
I don't know if this is still relevant to you: At least in Spring Data JPA 1.9.4 you can specify two queries.
Given a repository:
interface FoobarEntityRepository extends JpaRepository<FoobarEntity, Integer> {
Page findFoobarsSpecialQuery(String someParameter, final Pageable pageable);
}
You can add 2 native queries to your entity, one for the query itself and one for the count statement:
#Entity
#SqlResultSetMappings({
#SqlResultSetMapping(name = "SqlResultSetMapping.count", columns = #ColumnResult(name = "cnt"))
})
#NamedNativeQueries({
#NamedNativeQuery(
name = "FoobarEntity.findFoobarsSpecialQuery",
resultClass = DailyPictureEntity.class,
query = "Select * from foobars f where someValue = :someParameter "
),
#NamedNativeQuery(
name = "FoobarEntity.findFoobarsSpecialQuery.count",
resultSetMapping = "SqlResultSetMapping.count",
query = "Select count(*) as cnt from foobars f where someValue = :someParameter "
)
})
FoobarEntity {
}
The trick is to specify the count query with the suffix .count. This works also with the Spring Data #Query annotation.
Notice that you need a SQL result set mapping for the count query, though.
This works actually pretty nice.
This is description, given in spring data jpa documentation (http://docs.spring.io/spring-data/jpa/docs/1.8.0.M1/reference/html/)
Native queriesThe #Query annotation allows to execute native queries
by setting the nativeQuery flag to true. Note, that we currently don’t
support execution of pagination or dynamic sorting for native queries
as we’d have to manipulate the actual query declared and we cannot do
this reliably for native SQL.
JPQL abstracts SQL implementation and it's providers specifics, and makes it responsibility of ORM framework to generate correct SQL.
So by using Pagination in JPQL form, Spring just needs to generate correct JPQL, and it will be interpreted on ORM level to correct SQL.
While doing so with SQL, would imply that Spring knows how to generated correct SQL for the vast majorities of RDBMS, duplicating ORM functionality, which is too much overhead.
There is a way to use Pageable with native queries with the SpEL capacity of Spring data, it is mention here.
You can find an example in this repository.
/**
* #see DATAJPA-564
*/
#Query(
value = "select * from (select rownum() as RN, u.* from SD_User u) where RN between ?#{ #pageable.offset -1} and ?#{#pageable.offset + #pageable.pageSize}",
countQuery = "select count(u.id) from SD_User u", nativeQuery = true)
Page<User> findUsersInNativeQueryWithPagination(Pageable pageable);
The sort functionnality will not work properly if there is a subquery in the fromclause or your native query and you wish to apply a dynamic sort on it. The way it can be done is to move the subquery in the where clause.
Spring data will append to the end of your request " order by " if there is a Sort object in the Pageable. (with Spring data 1.10.3)
A better way is to convert the native query in jpql if possible.

Dynamic Named Query in Entity class using JPQL example

I have a named query as below;
#NamedQuery(name = "MyEntityClass.findSomething", query = "SELECT item FROM MyTable mytbl")
Now I want to append dynamic sort clause to this query (based on UI parameters)
Can I get an example using JPQL for doing the same (like how to set a dynamic ORDER BY in the Entity class)
I have already tried using CriteriaQuery, but was looking for a JPQL implementation now.
NamedQueries are by definition NOT dynamic, it is not correct to change them programmatically.
So the way to go is to create a JPQL query (but not a named query) like this:
TypedQuery<MyEntity> query = em.createdQuery("SELECT item FROM MyEntity item ORDER BY "+sortingCol, MyEntity.class);
On the other hand, if you REALLY want to use the named query, you could do that the following way:
#NamedQuery(name = "MyEntityClass.findSomething", query = MyEntity.NAMED_QUERY)
#Entity
public class MyEntity {
public static final NAMED_QUERY= "SELECT item FROM MyTable mytbl";
//+your persistent fields/properties...
}
//and later in your code
TypedQuery<MyEntity> query = entityManager.createQuery(MyEntity.NAMED_QUERY + " ORDER BY " + sortingCol, MyEntity.class);
Complementing for JPA 2.1
As of JPA 2.1 it is possible to define named queries programmatically.
This can be achieved using entityManagerFactory.addNamedQuery(String name, Query).
Example:
Query q = this.em.createQuery("SELECT a FROM Book b JOIN b.authors a WHERE b.title LIKE :title GROUP BY a");
this.em.getEntityManagerFactory().addNamedQuery("selectAuthorOfBook", q);
// then use like any namedQuery
Reference here
This can be useful, for instance, if you have the orderby field defined as a application parameter. So, when the application starts up or on the first run of the query, you could define the NamedQuery with the defined OrderBy field.
On the other side, if your OrderBy can be changed anytime (or changes a lot), then you need dynamic queries instead of NamedQuery (static). It would not worth to (re)create a NamedQuery every time (by performance).
#NamedQuery
Persistence Provider converts the named queries from JPQL to SQL at deployment time.
Until now, there is no feature to create/update the query with #NamedQuery annotation at runtime.
On the other hand, you can use Reflection API, to change the annotation value at runtime. I think It is not solution, also it is not you wanted .
em.createQuery()
Persistence Provider converts the dynamic queries from JPQL to SQL every time it is invoked.
The main advantage of using dynamic queries is that the query can be created based on the user inputs.

set table name with hibernate name parameters

I need to set a table name dynamically so that I use query.setText(tname,abc)
e.g: select a.name from :tname where a.id = '2'
I used setText() because when I use setString() it says "tname is a invalid parameter" because I assume that Hibernate adds '' when setting string parameters.
But even setText() does not help and gives the same exception.
How can I set the table name dynamically?
Reply to PSR:
So you mean replace table name as a java string replacement. But then we can not take support of sql injections prevention etc from hibernate right? Also How we bind parameters in hibernate in a situation where like statement,
Eg: name like "%:name%"
This also gives me Illegal argument exception: Parameter does not exist as a named parameter when i try to bind it using query.setString(name,"def");
Hibernate will not do this for you, because it works with PreparedStatements, and you can't prepare a statement where the table being queried isn't known yet.
I don't see why you would be exposing table names to end users, so preventing SQL injection doing a regular string substitution should be easy. You use some sort of business logic to determine the correct table from a list that only you know. The table name isn't coming from user input at all.
Depending on your choice of RDBMS, you may find a discriminator column, or table inheritance with partitioning to be a better way of handling a situation where identical queries are made against different tables.
It is not possible to set table name dynamically.You can set dynamically column names.it is not possible to set table name
try like this
select a.name from '+table name+'where a.id = '2'
In my opinion, There are 2 ways to resolve this issue:
1- If you are using Spring and Hibernate together, you could use SpEL and it would be like #{#entityName} as it is described here
#Entity
public class User {
#Id
#GeneratedValue
Long id;
String lastname;
}
public interface UserRepository extends JpaRepository<User,Long> {
#Query("select u from #{#entityName} u where u.lastname = ?1")
List<User> findByLastname(String lastname);
}
2-You could use CriteriaBuilder like
CriteriaQuery<YourEntity> cr = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cr.from(YourEntity.class);
cr.select(root);
I copied the source codes from the provided links and they are described there much better

Categories