SpringDataJPA: custom data mapping with Native Query - spring

public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?0", nativeQuery = true)
User findByEmailAddress(String emailAddress);
}
Let's say I have the code above where I select * from user. What should I do if I don't want this method to return User object. Is there a way I can manually map the data to a custom object MyUser? Can I do all this in the UserRepository interface?
Thanks!

You can do something like this
#Query(value = "SELECT YOUR Column1, ColumnN FROM USERS WHERE EMAIL_ADDRESS = ?0", nativeQuery = true)
List<Object[]> findByEmailAddress(String emailAddress);
You have to do the mapping. Take a look at the Spring Data Repository as well. Source

What about interface based projection?
Basically you write interface with getters that correspond to SQL query parameters.
In this way you even don't need to force #Id parameter on projection:
#Entity
public class Book {
#Id
private Long id;
private String title;
private LocalDate published;
}
public interface BookReportItem {
int getYear();
int getMonth();
long getCount();
}
public interface BookRepository extends Repository<Book, Long> {
#Query(value = "select " +
" year(b.published) as year," +
" month(b.published) as month," +
" count(b) as count," +
" from Book b" +
" group by year(b.published), month(b.published)")
List<BookReportItem> getPerMonthReport();
}
It uses org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap underneath as proxy for interface in current Spring implementation.
It works for nativeQuery = true too.

Related

How to return just the primary key of a JPA entity [duplicate]

I am using Spring JPA to perform all database operations. However I don't know how to select specific columns from a table in Spring JPA?
For example:
SELECT projectId, projectName FROM projects
You can use projections from Spring Data JPA (doc). In your case, create interface:
interface ProjectIdAndName{
String getId();
String getName();
}
and add following method to your repository
List<ProjectIdAndName> findAll();
I don't like the syntax particularly (it looks a little bit hacky...) but this is the most elegant solution I was able to find (it uses a custom JPQL query in the JPA repository class):
#Query("select new com.foo.bar.entity.Document(d.docId, d.filename) from Document d where d.filterCol = ?1")
List<Document> findDocumentsForListing(String filterValue);
Then of course, you just have to provide a constructor for Document that accepts docId & filename as constructor args.
You can set nativeQuery = true in the #Query annotation from a Repository class like this:
public static final String FIND_PROJECTS = "SELECT projectId, projectName FROM projects";
#Query(value = FIND_PROJECTS, nativeQuery = true)
public List<Object[]> findProjects();
Note that you will have to do the mapping yourself though. It's probably easier to just use the regular mapped lookup like this unless you really only need those two values:
public List<Project> findAll()
It's probably worth looking at the Spring data docs as well.
In my situation, I only need the json result, and this works for me:
public interface SchoolRepository extends JpaRepository<School,Integer> {
#Query("select s.id, s.name from School s")
List<Object> getSchoolIdAndName();
}
in Controller:
#Autowired
private SchoolRepository schoolRepository;
#ResponseBody
#RequestMapping("getschoolidandname.do")
public List<Object> getSchool() {
List<Object> schools = schoolRepository.getSchoolIdAndName();
return schools;
}
With the newer Spring versions One can do as follows:
If not using native query this can done as below:
public interface ProjectMini {
String getProjectId();
String getProjectName();
}
public interface ProjectRepository extends JpaRepository<Project, String> {
#Query("SELECT p FROM Project p")
List<ProjectMini> findAllProjectsMini();
}
Using native query the same can be done as below:
public interface ProjectRepository extends JpaRepository<Project, String> {
#Query(value = "SELECT projectId, projectName FROM project", nativeQuery = true)
List<ProjectMini> findAllProjectsMini();
}
For detail check the docs
In my case i created a separate entity class without the fields that are not required (only with the fields that are required).
Map the entity to the same table.
Now when all the columns are required i use the old entity, when only some columns are required, i use the lite entity.
e.g.
#Entity
#Table(name = "user")
Class User{
#Column(name = "id", unique=true, nullable=false)
int id;
#Column(name = "name", nullable=false)
String name;
#Column(name = "address", nullable=false)
Address address;
}
You can create something like :
#Entity
#Table(name = "user")
Class UserLite{
#Column(name = "id", unique=true, nullable=false)
int id;
#Column(name = "name", nullable=false)
String name;
}
This works when you know the columns to fetch (and this is not going to change).
won't work if you need to dynamically decide the columns.
In my opinion this is great solution:
interface PersonRepository extends Repository<Person, UUID> {
<T> Collection<T> findByLastname(String lastname, Class<T> type);
}
and using it like so
void someMethod(PersonRepository people) {
Collection<Person> aggregates =
people.findByLastname("Matthews", Person.class);
Collection<NamesOnly> aggregates =
people.findByLastname("Matthews", NamesOnly.class);
}
I guess the easy way may be is using QueryDSL, that comes with the Spring-Data.
Using to your question the answer can be
JPAQuery query = new JPAQuery(entityManager);
List<Tuple> result = query.from(projects).list(project.projectId, project.projectName);
for (Tuple row : result) {
System.out.println("project ID " + row.get(project.projectId));
System.out.println("project Name " + row.get(project.projectName));
}}
The entity manager can be Autowired and you always will work with object and clases without use *QL language.
As you can see in the link the last choice seems, almost for me, more elegant, that is, using DTO for store the result. Apply to your example that will be:
JPAQuery query = new JPAQuery(entityManager);
QProject project = QProject.project;
List<ProjectDTO> dtos = query.from(project).list(new QProjectDTO(project.projectId, project.projectName));
Defining ProjectDTO as:
class ProjectDTO {
private long id;
private String name;
#QueryProjection
public ProjectDTO(long projectId, String projectName){
this.id = projectId;
this.name = projectName;
}
public String getProjectId(){ ... }
public String getProjectName(){....}
}
Using Spring Data JPA there is a provision to select specific columns from database
---- In DAOImpl ----
#Override
#Transactional
public List<Employee> getAllEmployee() throws Exception {
LOGGER.info("Inside getAllEmployee");
List<Employee> empList = empRepo.getNameAndCityOnly();
return empList;
}
---- In Repo ----
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
#Query("select e.name, e.city from Employee e" )
List<Employee> getNameAndCityOnly();
}
It worked 100% in my case.
Thanks.
You can use JPQL:
TypedQuery <Object[]> query = em.createQuery(
"SELECT p.projectId, p.projectName FROM projects AS p", Object[].class);
List<Object[]> results = query.getResultList();
or you can use native sql query.
Query query = em.createNativeQuery("sql statement");
List<Object[]> results = query.getResultList();
You can apply the below code in your repository interface class.
entityname means your database table name like projects.
And List means Project is Entity class in your Projects.
#Query(value="select p from #{#entityName} p where p.id=:projectId and p.projectName=:projectName")
List<Project> findAll(#Param("projectId") int projectId, #Param("projectName") String projectName);
It is possible to specify null as field value in native sql.
#Query(value = "select p.id, p.uid, p.title, null as documentation, p.ptype " +
" from projects p " +
"where p.uid = (:uid)" +
" and p.ptype = 'P'", nativeQuery = true)
Project findInfoByUid(#Param("uid") String uid);
You can use the answer suggested by #jombie, and:
place the interface in a separate file, outside the entity class;
use native query or not (the choice depended on your needs);
don't override findAll() method for this purpose but use name of your choice;
remember to return a List parametrized with your new interface (e.g. List<SmallProject>).
Using Native Query:
Query query = entityManager.createNativeQuery("SELECT projectId, projectName FROM projects");
List result = query.getResultList();
public static final String FIND_PROJECTS = "select ac_year_id,ac_year from tbl_au_academic_year where ac_year_id=?1";
#Query(value = FIND_PROJECTS, nativeQuery = true)
public List<Object[]> findByAcYearId(Integer ac_year_id);
this works for me
You can update your JPARepository as below.
#Query("select u.status from UserLogin u where u.userId = ?1 or u.email = ?1 or u.mobile = ?1")
public UserStatus findByUserIdOrEmailOrMobile(String loginId);
Where UserStatus is a Enum
public enum UserStatus
{
New,
Active,
Deactived,
Suspended,
Locked
}
Use:
#Query("SELECT e FROM #{#entityName} e where e.userId=:uid")
List<ClienteEnderecoEntity> findInfoByUid(#Param("uid") UUID uid);
{
"Comments":"Why not using JDBCTemplate",
"Url":"https://www.baeldung.com/spring-jdbc-jdbctemplate"
}

Spring Data: Get Count With Additional Fields

I want to return a count of one field plus another field. But I cant transform it into POJO. I can get it only as List of object array. What to I have:
public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long> {
#Query(value = "select count(c.is_read), c.chat_id from chat_message c where (c.sender_id = :userId or c.receiver_id = :userId) group by c.chat_id", nativeQuery = true)
List<Object[]> findAllByUserId(long userId);
But I need something like this:
#Query(value = "select count(c.is_read), c.chat_id from chat_message c where (c.sender_id = :userId or c.receiver_id = :userId) group by c.chat_id", nativeQuery = true)
List<POJO> findAllByUserId(long userId);
#Data
public static class POJO {
private long count;
private long id;
}
Found the answer:
public interface POJO {
Long getCount();
Long getId();
}
#Query(value = "select count(c.isRead) as count,c.chat.chatId as id from ChatMessage c where (c.senderId = :userId or c.receiverId= :userId) group by c.chat.chatId ")
List<POJO> findAllByUserId(long userId);

JpaRepository - Inner Join - Subsequent selects

I'm facing a problem when I try to get an list of ServiceCup with its ServiceLanguage. When I try to manipulate the list of ServiceCup in my service layer hibernate is executing a second query and populate my ServiceCup with all ServiceLanguage again.
ServiceCup x ServiceLanguage x LanguageCup
ServiceCup:
#Data
#Entity
#Table(name = "csm_service")
public class ServiceCup extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private String context;
// bi-directional many-to-one association to CsmServiceLanguage
#OneToMany(mappedBy = "service")
private List<ServiceLanguage> serviceLanguages;
}
ServiceLanguage:
#Data
#Entity
#Table(name = "csm_service_language")
public class ServiceLanguage extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "translated_name")
private String translatedName;
// bi-directional many-to-one association to CsmLanguage
#ManyToOne
#JoinColumn(name = "csm_language_id_fk")
private LanguageCup language;
// bi-directional many-to-one association to CsmService
#ManyToOne
#JoinColumn(name = "csm_service_id_fk")
private ServiceCup service;
}
JpaRepository:
#Query(value = "select s, sl from ServiceCup s \n" + "INNER JOIN FETCH ServiceLanguage sl on s.id = sl.service \n"
+ "where sl.language.id = :languageId")
List<ServiceCup> findAllServicesByLanguageId(#Param("languageId") String languageId);
Query in repository layer:
select *all_fields* from csm_service servicecup0_ inner join csm_service_language servicelan1_ on (servicecup0_.id=servicelan1_.csm_service_id_fk) where servicelan1_.csm_language_id_fk=?
But in service layer execute a lot of queries to bring all the relations of ServiceCup. I want the ServiceCup objects populate but only with the results that are in the query.
How can I get a ServiceCup object with only the results of the query?
PS: In my method in service layer I have #Transactional(readOnly = true) but if I remove I can't get the objects related to ServiceCup.
I needed to use projection and not the entity to execute the query only once.
ServiceCupProj
public interface ServiceCupProj {
public Long getId();
public String getDescription();
public String getInternalname();
......
Repository
#Repository
public interface CupServiceRepository extends JpaRepository<ServiceCup, Long> {
#Query(value = "select servicecup0_.id as id, servicelan1_.translated_description as description, servicelan1_.translated_name as internalname \n"
+ "from csm_service servicecup0_ inner join csm_service_language servicelan1_ on (servicecup0_.id=servicelan1_.csm_service_id_fk) \n"
+ "where servicelan1_.csm_language_id_fk = :languageId ", nativeQuery = true)
List<ServiceCupProj> findAllServicesByLanguageId(#Param("languageId") String languageId);
......
And in the service layer I transform this ServiceCupProj in the entity that I need.

How to fetch only selected attributes of an entity using Spring JPA?

I'm using Spring Boot (1.3.3.RELEASE) and Hibernate JPA in my project. My entity looks like this:
#Data
#NoArgsConstructor
#Entity
#Table(name = "rule")
public class RuleVO {
#Id
#GeneratedValue
private Long id;
#Column(name = "name", length = 128, nullable = false, unique = true)
private String name;
#Column(name = "tag", length = 256)
private String tag;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleOutputArticleVO> outputArticles;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleInputArticleVO> inputArticles;
}
My repository looks like this:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
}
In some cases I need to fetch only id and name attributes of entity RuleVO. How can I achieve this? I found a notice it should be doable using Criteria API and Projections but how? Many thanks in advance. Vojtech
UPDATE:
As has been pointed out to me, I'm lazy and this can very well be done hence I'm updating my answer after having looked around the web for a proper one.
Here's an example of how to get only the id's and only the names:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT r.id FROM RuleVo r where r.name = :name")
List<Long> findIdByName(#Param("name") String name);
#Query("SELECT r.name FROM RuleVo r where r.id = :id")
String findNameById(#Param("id") Long id);
}
Hopefully this update proves helpful
Old Answer:
Only retrieving the specific attributes name/id is not possible as this is not how spring was designed or any SQL database for that matter as you always select a row which is an entity.
What you CAN do is query over the variables in the entity, for instance:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public RuleVo findOneByName(String name);
public RuleVo findOneByNameOrId(String name, Long id);
public List<RuleVo> findAllByName(String name);
// etc, depending on what you want
}
You can modify these however you want w.r.t. your needs. You can call these methods directly via the autowired repository
See http://docs.spring.io/spring-data/jpa/docs/current/reference/html/ Section 5.3 for more options and examples
interface IdOnly{
String getId();
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public List<IdOnly> findAllByName(String name);
}
I notice that this is a very old post, but if someone is still looking for an answer, try this. It worked for me.
You can also define custom constructor to fetch specific columns using JPQL.
Example:
Replace {javaPackagePath} with complete java package path of the class
use as a constructor in JPQL.
public class RuleVO {
public RuleVO(Long id, String name) {
this.id = id;
this.name = name;
}
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT new {javaPackagePath}.RuleVO(r.id, r.name) FROM RuleVo r where r.name = :name")
List<RuleVO> findIdByName(#Param("name") String name);
}
Yes, you can achieve it with projections. You have many ways to apply them:
If you could upgrade to Spring Data Hopper, it provides an easy to use support for projections. See how to use them in the reference documentation.
Otherwise, first of all create a DTO with the attributes you want to load, something like:
package org.example;
public class RuleProjection {
private final Long id;
private final String name;
public RuleProjection(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
Of course, you could use Lombok annotations also.
Then, you can use in the JPQL queries like this:
select new org.example.RuleProjection(rule.id, rule.name) from RuleVO rule order by rule.name
Another option, if you want to avoid using DTO class names in your queries, is to implement your own query method using QueryDSL. With Spring Data JPA, you have to:
Create a new interface with the new method. Ex:
public interface RuleRepositoryCustom {
public List<RuleProjection> findAllWithProjection();
}
Change your repository to extend the new interface. Ex:
public interface RuleRepository extends JpaRepository<RuleVO, Long>, RuleRepositoryCustom {
...
Create an implementation of the Custom repository using the Spring Data JPA QueryDSL support. You have to previously generate the Q clases of QueryDSL, using its Maven plugin. Ex:
public class RuleRepositoryImpl {
public List<RuleProjection> findAllWithProjection() {
QRuleVO rule = QRuleVO.ruleVO;
JPQLQuery query = getQueryFrom(rule);
query.orderBy(rule.name.asc());
return query.list(ConstructorExpression.create(RuleProjection.class, rule.id, rule.name));
}
}
You can do it by using #Query annotation(HQL).
Please refer to the Spring docs below:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
(search for #Query in spring document)

Define #Query on base repository interface

Is there a way to declare a #Query on a base repsitory interface so that you don't have to declare it in all repositories? The query would have different entity names in the "FROM" part of the query.
#MappedSuperclass
public abstract class BaseAction {
#Id
Long id;
...
}
#Entity
#Table(name="AKTION_EMAIL")
public class EmailAction extends BaseAction {
private String email;
}
public interface ActionRepository<T extends BaseAction> extends JpaRepository<T, ActionPK> {
#Query("SELECT max(seqNumber) + 1 FROM ????????????? e WHERE e.order = ?1 AND e.action = ?2")
Long findNextSeqNumberByOrderAndAction(Order order, ActionConfiguration action);
}
public interface EmailActionRepository extends ActionRepository<EmailAction> {
// This works, but I don't want to repeat that in all entity repositories...
#Query("SELECT max(seqNumber) + 1 FROM EmailAction e WHERE e.order = ?1 AND e.action = ?2")
Long findNextSeqNumberByOrderAndAction(Order order, ActionConfiguration action);
}
You can use a SpEL expression in the generic query definition to refer to the unknown entity type:
interface ActionRepository<T extends BaseAction> extends JpaRepository<T, ActionPK> {
#Query("SELECT max(seqNumber) + 1 FROM #{#entityName} e WHERE …")
Long findNextSeqNumberByOrderAndAction(Order ActionConfiguration action);
}
Note how we use #{#entityName} to dynamically insert the name of the entity that re repository will be created for.

Resources