Change schema of query dynamically based on the connection DEV/SIT/PROD - java

I have a native query in Spring Data JPA:
#Repository
public interface BookRepository extends JpaRepository<Book, Integer>{
#Query(value = "select * from DEV.Book where find_in_set(market)", nativeQuery = true)
public List<Book> findBooksByMarcket(#Param("market") String market);
}
Now I want to change the DEV.Book based on my connection SIT/PROD dynamically, based on the connection I tried to pass a string into native query which didn't work. What is the best way to do it?

You should set Schema in your connection and leave the query without schema.

You should find some way to remove the namespace from the query.
You could for example have three databases without any namespace. If you configure the database connection via properties you can easily switch by supplying different startup properties or property files.

You can use it in this way:
#Override
public List<Book> findBooksByMarcket(String market) {
TypedQuery query = em.createNativeQuery("select * from DEV.Book where find_in_set(?)", Book.class);
query.setParameter(1, market);
return query.getResultList();
}

Related

Mapping #Query with multiple column selection to an Java object in a #Repository - is it possible out of the box?

Is it possible to map the results of a Hibernate #Query like this (in a #Repository interface extending #JpaRepository):
#Query("select u.id, u.email, u.status from user u")
public SimpleUserDTO getAllUsersSimpleData();
directly to a Java object like this:
public class SimpleUserDTO {
private Long id;
private String email;
private String status;
}
What I know, is that doing something like this:
Query query = session.createQuery("select u.id, u.email, u.status from user u");
the result can be extracted to a List<Object[]>:
List<Object[]> users = (List<Object[]>) query.list();
But is it possible to map it directly to a List<SimpleUserDTO> without writing additional method that will map the values to SimpleUserDTO?
You have two options.
Option 1: Constructor Expression:
#Query("select new <insert_package_here>.SimpleUserDTO(u.id, u.email, u.status) from user u")
public List<SimpleUserDTO> getAllUsersSimpleData();
Option 2: Use Interface projection
Turn your DTO into an interface and you can use it without #Query annotation
public List<SimpleUserDTO> getAllUsersSimpleData();
Please find out more about projections and Spring Data JPA in the documentation:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

how to handle JSON_VAL() result set in CrudRepository hibernate

Could some one help me how to read result set from JSON_VAL() in db2. I am having the below db2 query which am executing from CrudRepository named query from hibernate.
SELECT SYSTOOLS.BSON2JSON(data) FROM TABLE WHERE JSON_VAL(data,'column','s:25')=:value
Could some one help me how can I read the value.
My Hibernate repository class is
#Repository
public interface MyRepository extends CrudRepository<MyClass, Integer> {
#Query(value = "SELECT SYSTOOLS.BSON2JSON(data) FROM TABLE WHERE JSON_VAL(data,'column','s:25')=:value", nativeQuery = true)
com.ibm.db2.jcc.DB2Clob findById(#Param("idd") String id);
}
thanks in advance .

Spring JPA selecting from where clause

I am using Spring JPA to perform all database operations. However I don't know how to select specific rows (connected by simple WHERE clause) from a table in Spring JPA?
For example:
SELECT * FROM user where name=agrawalo AND email=abc#example.com
User Class:
#Entity
Class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String email;
// Getters and Setters
}
Repository:
public interface UserRepository extends JpaRepository<User, Integer> {
}
You don't need to write queries for such simple things if you are using spring-data-jpa. you can write a method name and spring-data will formulate a query based on your method name and get the results.
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByNameAndEmail(String name, String email)
}
Create a method like above and call the method with the required arguments.
If you don't want(not advisable) to use Optional, you can just use User as return type. In such case, if there are no entries matching your arguments then you would have null returned.
public interface UserRepository extends JpaRepository<User, Integer> {
public User findUserByNameAndEmail(String name,String email);
}
Implementation will be created on the fly.
I know I am very very late to this but I still want to provide another solution that one would like to use. This is particularly useful when you think the queries generated by method names do not serve the purpose that you want and you really want to write the native queries. To do that, you can actually use the #Query annotation in your repository and put your query inside it like below:
#Query(value = "SELECT * FROM user where name = ?1 AND email = ?2", nativeQuery = true)
List<User> getUserByNameAndEmail(String name, String email);
Here the #Query tells that the query provided in the value attribute needs to be executed and the nativeQuery attribute tells that it is a native sql query.
Notice that values for name and email are provided as ?1 and ?2 respectively which means these are placeholders and will be replaced by the parameters that getUserByNameAndEmail repository method will receive at runtime in variables name and email.
Simply you can declare below method in you repository interface, implementation will be taken care by Spring-data-jpa
User findByNameAndEmail(String name, String email);

How to use createQuery with spring boot

Normally I use annotiations:#Query("SELECT c FROM Country c") with JpaRepositoryor predefined methods like findAll
but in my case I want to generate dynamic query.
String baseQuery =SELECT c FROM Country c`
if(age!=null)
baseQuery+="WHERE c.age=20"
I need to perform same query from code level like this:
Query q1 = em.createQuery("SELECT c FROM Country c");
but I dont use EntityManager in spring boot
How can I generate query from code level?
If you would like to create dynamic queries from code you can take advantage of Spring's JdbcTemplate. Using spring boot it is as simple as injecting JdbcOperations bean to your repository class (assuming you have provided spring-boot-starter-jdbc module to your project).
But remember! This solution uses SQL, not JPQL. That's why you have to use proper tables and columns names in queries and properly map result to objects (i.e. using RowMapper)
This simple example worked fine for me (with different entity, but in same manner - I've adapted it to your example):
#Repository
public class CountryRepository {
#Autowired
private JdbcOperations jdbcOperations;
private static String BASIC_QUERY = "SELECT * FROM COUNTRY";
public List<Country> selectCoutry(Long age){
String query = BASIC_QUERY;
if (age != null){
query += " WHERE AGE = ";
query += age.toString();
}
//let's pretend that Country has constructor Conutry(String name, int age)
return jdbcOperations.query(query, (rs, rowNum) ->
{ return new Country(rs.getString("NAME"), rs.getInt("AGE");}
);
};
}
Then in service or whatever you inject CountryRepository and call method.
Since you're using Spring Boot, you can use Spring Data to create queries in your repository:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
}
Not a 100% on syntax, but should be something similar.
Now you can autowire this class:
#Autowired
public CountryRepository countryRepo;
And all basic methods are already available to you like:
countryRepo.findOne(id);
countryRepo.find();
If you want to make more advanced queries, you can use Spring Data e.g.:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
public Country findByNameAndContinent(String name, String continent);
}
This is just an example (a stupid one) of course and assumes your Country class has the field names 'name' and 'continent' and both are strings. More is available here:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Section 5.3 more specifically.
PS: Make sure your Country class has the #Entity annotation

How to use #NamedQuery in spring a CrudRepository #Query?

I want to make use of a #NamedQuery inside a JpaRepository. But it does not work:
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
#Query(name = MyEntity.FIND_ALL_CUSTOM)
List<MyEntity> findAllCustom(Pageable pageable);
}
#Entity
#NamedQuery(
name = MyEntity.FIND_ALL_CUSTOM, query = "select * from MyEntity me where me.age >= 18"
)
public class MyEntity {
public static final String FIND_ALL_CUSTOM = "findAllCustom";
}
Result:
org.springframework.data.mapping.PropertyReferenceException: No property findAllCustom found for type MyEntity!
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:75)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:327)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:307)
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:270)
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:241)
at org.springframework.data.repository.query.parser.Part.<init>(Part.java:76)
at org.springframework.data.repository.query.parser.PartTree$OrPart.<init>(PartTree.java:235)
at org.springframework.data.repository.query.parser.PartTree$Predicate.buildTree(PartTree.java:373)
at org.springframework.data.repository.query.parser.PartTree$Predicate.<init>(PartTree.java:353)
at org.springframework.data.repository.query.parser.PartTree.<init>(PartTree.java:84)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:61)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:94)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:205)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:72)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:369)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:192)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:239)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:225)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1633)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
... 28 more
Update:
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
List<MyEntity> findAllCustom(Pageable pageable);
}
#Entity
#NamedQuery(
name = "MyEntity.findAllCustom", query = "select * from MyEntity me where me.age >= 18"
)
public class MyEntity {
}
Still same exception:
PropertyReferenceException: No property findAllCustom found for type MyEntity!
Take a look at the documentation of Spring Data JPA - Using JPA NamedQueries.
I advise you follow the conventions set in the documentation (starting with the simple name of the configured domain class, followed by the method name separated by a dot). Cut the underscore and name the query like
#NamedQuery(name = "MyEntity.findAllCustom", query="...")
or even better add a suggestive name like findByAge or sth.
To allow execution of these named queries all you need to do is to specify MyEntityRepository as follows:
public interface MyEntityRepository extends JpaRepository <MyEntity, Long> {
List<MyEntity> findAllCustom();
}
I implemented it with the JpaRepository as the documentation exemplifies. But you could try with a simple CrudRepository and see if that works.
I think the problem was you where using #Query and the Queries annotated to the query method will take precedence over queries defined using #NamedQuery. Read the docs for the #Query usage, i think you where also using it wrong.
Update
To use the Pageable, according to this answer
to apply pagination, a second subquery must be derived. Because the
subquery is referring to the same fields, you need to ensure that your
query uses aliases for the entities/tables it refers to
that means you would rewrite your query like
query ="select * from MyEntity me where me.age >= 18".
The example was used for #Query, but that is also a named query so it should apply to your case as well. The only difference is that with #Query you actually bind them directly rather than annotating them to the domain class.
Update 2
I tried in my own app.
First off you should have the query using the alias instead of * (i.e me).
Secondly the string you use FIND_ALL_CUSTOM is not following the convention which is "MyEntity.findAllCustom".
Solution
Copy paste this:
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
List<MyEntity> findAllCustom(Pageable pageable);
List<MyEntity> findAllCustom();
}
#Entity
#NamedQuery(
name = MyEntity.FIND_ALL_CUSTOM, query = "select me from MyEntity me where me.age >= 18"
)
public class MyEntity {
public static final String FIND_ALL_CUSTOM = "MyEntity.findAllCustom";
}
Both will work. For the one with the pageable method argument call it as myEntityRepository.allCustom(new PageRequest(0,20)). Ofc, you know that myEntityRepository is injected.

Categories

Resources