How return column from my DB using Java Spring JPQL - spring-boot

I have a very basic task. I want to return just one column from my table in my DB. Literally, I want the text from my category_name. This is my JPQL cod:|
#Transactional
#Modifying
#Query(value = "SELECT category_name FROM Category WHERE id=:id", nativeQuery = true)
String findName(#Param("id") long id);
And I have this error:
Modifying queries can only use void or int/Integer as return type!

As the error apparently states, you should use #Modifying annotation when you are actually updating/deleting the row. Since you are fetching data from already stored database, you can simply remove this annotation.
You should also remove #Transactional annotation.
https://dzone.com/articles/how-does-spring-transactional is an interesting article to know about how transactional annotation works and when it should be used.

Related

Pass SQL statement to JPA #Query

Is there a way to pass the whole sql statement or a part of it to #Query annotation?
#Modifying
#Query(value = "INSERT INTO my_table :query", nativeQuery = true)
#Transactional
void test(#Param("query") String query);
I think a viable option for you would be to use the entity manager, instead of the repository. You can do it using NativeQuery and its executeUpdate method. Please check:
Inserting in my JPA using entity manager native query

How to pass pageable for native query?

I was doing a project and there i had a requirement of using pageable object and recieved page object from JPA.
Does anyone have any idea on how to use this?
#Repository
public interface CustomerRepository extends JpaRepository<Customer,Long>{
#Query("SELECT * FROM WHERE name=?1 AND surname=?2 ", nativeQuery = true)
List<Customer> findAllByNameAndSurname(String name,String surname);
}
I want a page List for result fetch from this query.
Spring Data JPA and native queries with pagination is not supported in Spring. According to documentation , Spring Data JPA repositories can be used with native queries and pagination. But, in real life Spring Data JPA (1.10.2.RELEASE) requires some additional fix.
You have to use this if you want pagination support.
List<Customer> customers = customerRepository.findAllByNameAndSurname(name,username);
PagedListHolder<Customer> pages = new PagedListHolder(customers);
pages.setPage(currentPageNumber); //set current page number
pages.setPageSize(pageSize); // set the size of page
pages.getPageList(); // return the list of items(or in your case List<Customer> ) of current page
Try this:
#Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
Page<Customer> findAllByNameEqualsAndSurnameEquals(String name, String surname, Pageable pageable);
}
I am pretty sure JpaRepository can handle your Pageable parameter.
Also, method name has to be as I mentioned as spring creates queries based on method name (uses reflection).
If you really need to execute NATIVE QUERY you will have to find other solution but I do not recommend the one provided by Dasari Swaroop Kumar as it just queries all objects from database and then kinda filters it in memory.
Also to that native query solution - you can extend your method definition to additional 2 parameters for page and pageSize and append them to your native query and leave repository to return plain List and then create PageImpl object in the layer that calls your CustomerRepository object.

How to query more than one columns but not all columns with #Query but still use the Domain Data Model to map with Spring Data JDBC?

My Data model is
#Getter
#Setter
public class Customer {
#Id private ID id;
#CreatedDate protected Instant createdAt;
#LastModifiedDate protected Instant updatedAt;
#CreatedBy protected String createdBy;
#LastModifiedBy protected String updatedBy;
#Version protected Long version;
private UUID orderId;
private String offer;
}
My Repository is
public interface CustomerRepository extends CrudRepository<Customer, UUID> {
#Query(
"SELECT ID, Offer FROM Customer WHERE orderId = :orderId ")
List<Customer> findCustomerByOrderId(
#Param("orderId") UUID orderId);
}
This will result in an exception saying 'orderId column not found [42122-190]'. So Spring expects you to always query all the columns. I understand that with JPA we have a strong mapping between the Entities and the Data Schema. But the whole point of spring data JDBC is avoiding the tight coupling between POJO's data model and database schema. Why not the EntityRowMapper is just mapping NULL to the properties which are not part of the query?
Is there a way to tell the RowMapper used, to ignore properties which are not part of the query? Creating separate RowMapper for these simple queries seems a lot of unnecessary work.
I still can work around this by changing the query like
#Query(
"SELECT ID, Offer, OrderId, null as CreatedAt, null as CreatedBy, null as UpdatedAt, null as UpdatedBy, null as Version FROM Customer WHERE orderId = :orderId ")
But this will still serialize the entire object with null values. Am I missing something obvious here?
Note This is not Spring Data JPA. Its Spring Data JDBC.
Edit
Looking more into it, the exception is from h2 database lib.
Caused by: org.h2.jdbc.JdbcSQLException: Column "orderid" not found [42122-190]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.jdbc.JdbcResultSet.getColumnIndex(JdbcResultSet.java:3129)
at org.h2.jdbc.JdbcResultSet.get(JdbcResultSet.java:3217)
at org.h2.jdbc.JdbcResultSet.getObject(JdbcResultSet.java:522)
at com.zaxxer.hikari.pool.HikariProxyResultSet.getObject(HikariProxyResultSet.java)
at org.springframework.data.jdbc.core.EntityRowMapper.readFrom(EntityRowMapper.java:127)
You can't at least right now.
There are three solutions to this, two of which you already pointed out:
extend your select statement with , NULL as <column-name> for all the missing columns.
I'm not sure if
But this will still serialize the entire object with null values.
means that this isn't working for you in some way.
specify a RowMapper.
You could use a class containing exactly the fields returned by the query. It could even have getters for the other columns if you want an interface implemented by both your normal entity and the partial entity.
You write:
But the whole point of spring data JDBC is to avoid the tight coupling between pojo's data model and database schema.
This is not quite right.
An important goal of Spring Data JDBC is to not have a run time connection between entities and table rows.
This would require proxies or similar and brings a lot of complexity.
But the structural mapping between entities and table is probably going to be stronger (and certainly is right now) since all the variants of mappings available in JPA bring complexity.
And the main goal in Spring Data JDBC is to be conceptually simpler than JPA.
You also ask
Why not the EntityRowMapper is just mapping NULL to the properties which are not part of the query?
I'm not sure if I actively thought about it when I coded it but I don't like the idea of defaulting to NULL because this would make it easy to accidentally not load a column because you have a typo in an alias.
But I'm not against alternative solutions.
If you have an idea please create a feature request.

Spring Boot + JPA - CrudRepository update and then read

I have a scenario where the need is to update some db records first based on a criteria and then read those records from db. I am using CrudReposirtory and in my controller I have a service which calls a repository method using a #Query to update the records and on the next line am trying to read the same records but the records are not updated unless the I am out of that controller method.
You should use #Modifying in conjunction with #Query if you perform an UPDATE statement.
From documentation:
#Modifying
#Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);
This will trigger the query annotated to the method as updating query instead of a selecting one. As the EntityManager might contain outdated entities after the execution of the modifying query, we automatically clear it (see JavaDoc of EntityManager.clear() for details). This will effectively drop all non-flushed changes still pending in the EntityManager. If you don't wish the EntityManager to be cleared automatically you can set #Modifying annotation's clearAutomatically attribute to false;

Spring Data: "delete by" is supported?

I am using Spring JPA for database access. I am able to find examples such as findByName and countByName, for which I dont have to write any method implementation. I am hoping to find examples for delete a group of records based on some condition.
Does Spring JPA support deleteByName-like delete? Any pointer is appreciated.
Regards and thanks.
Deprecated answer (Spring Data JPA <=1.6.x):
#Modifying annotation to the rescue. You will need to provide your custom SQL behaviour though.
public interface UserRepository extends JpaRepository<User, Long> {
#Modifying
#Query("delete from User u where u.firstName = ?1")
void deleteUsersByFirstName(String firstName);
}
Update:
In modern versions of Spring Data JPA (>=1.7.x) query derivation for delete, remove and count operations is accessible.
public interface UserRepository extends CrudRepository<User, Long> {
Long countByFirstName(String firstName);
Long deleteByFirstName(String firstName);
List<User> removeByFirstName(String firstName);
}
Derivation of delete queries using given method name is supported starting with version 1.6.0.RC1 of Spring Data JPA. The keywords remove and delete are supported. As return value one can choose between the number or a list of removed entities.
Long removeByLastname(String lastname);
List<User> deleteByLastname(String lastname);
2 ways:-
1st one Custom Query
#Modifying
#Query("delete from User where firstName = :firstName")
void deleteUsersByFirstName(#Param("firstName") String firstName);
2nd one JPA Query by method
List<User> deleteByLastname(String lastname);
When you go with query by method (2nd way) it will first do a get call
select * from user where last_name = :firstName
Then it will load it in a List
Then it will call delete id one by one
delete from user where id = 18
delete from user where id = 19
First fetch the list of object, then for loop to delete id one by one
But, the 1st option (custom query),
It's just a single query
It will delete wherever the value exists.
Since in 2nd option it is making multiple DB query, try to use the first option.
Go through this link too https://www.baeldung.com/spring-data-jpa-deleteby
If you take a look at the source code of Spring Data JPA, and particularly the PartTreeJpaQuery class, you will see that is tries to instantiate PartTree.
Inside that class the following regular expression
private static final Pattern PREFIX_TEMPLATE = Pattern.compile("^(find|read|get|count|query)(\\p{Lu}.*?)??By")
should indicate what is allowed and what's not.
Of course if you try to add such a method you will actually see that is does not work and you get the full stacktrace.
I should note that I was using looking at version 1.5.0.RELEASE of Spring Data JPA
If you will use pre defined delete methods as directly provided by spring JPA then below two queries will be execute by the framework.
First collect data(like id and other column) using by execute select query with delete query where clause.
then after getting resultSet of first query, second delete queries will be execute for all id(one by one)
Note : This is not optimized way for your application because many queries will be execute for single MYSQL delete query.
This is another optimized way for delete query code because only one delete query will execute by using below customized methods.
#NamedNativeQueries({
#NamedNativeQuery(name = "Abc.deleteByCreatedTimeBetween",
query = "DELETE FROM abc WHERE create_time BETWEEN ?1 AND ?2")
,
#NamedNativeQuery(name = "Abc.getByMaxId",
query = "SELECT max(id) from abc")
})
#Entity
public class Abc implements Serializable {
}
#Repository
public interface AbcRepository extends CrudRepository {
int getByMaxId();
#Transactional
#Modifying
void deleteByCreatedTimeBetween(String startDate, String endDate);
}
It works just
import org.springframework.transaction.annotation.Transactional;
#Transactional
Long removeAddressByCity(String city);
Yes , deleteBy method is supported
To use it you need to annotate method with #Transactional
here follows my 2 cents. You can also use native queries, like:
#Modifying
#Query(value="delete from rreo r where r.cod_ibge = ?1 and r.exercicio= ?2", nativeQuery = true)
void deleteByParameters(Integer codIbge, Integer exercicio);
#Query(value = "delete from addresses u where u.ADDRESS_ID LIKE %:addressId%", nativeQuery = true)
void deleteAddressByAddressId(#Param("addressId") String addressId);

Resources