(Spring/JpaRepository ) Dynamic #Query, when Inheriting methods of JpaRepository from BaseEntityRepository to SubEntityRepository - spring

(This post continues the discussion from, the important parts are all repeated in-here:
(Spring/JpaRepository ) Inheriting methods of JpaRepository from BaseEntityRepository to SubEntityRepository)
Let's assume we have the following entities:
#Entity public class BaseEntity { }
#Entity public class SubEntity extends BaseEntity { }
and the following JpaRepository implemenations:
public interface BaseEntityRepository<T, I> extends JpaRepository<TableWithId, Long> {
#Query("SELECT t FROM BaseEntity t WHERE id = :id")
Optional<T> getById(#Param("id") Long id);
#Query("SELECT t FROM BaseEntity t WHERE customField = :customField")
List<T> findByCustomField(#Param("customField") String customField);
}
Now for the SubEntity I have another repo:
public interface SubEntityRepository extends BaseEntityRepository<SubEntity, Long> {}
Will JPA know that in #Query "BaseEntity" must be replaced by "SubEntity" and why?
If "no", how would be the best way to do what I want?

You need to use SPEL
#Query("SELECT T FROM #{#entityName} T WHERE T.id = :id")
Optional<T> getById(#Param("id") Long id);

Related

Hibernate Spring repository inheritance add more details to an Entity

I'm using hibernate and spring repository. I have 2 classes Person and PersonDetails which contains more details about the person.
#Entity
#Table(name = "person")
#Inheritance(strategy = InheritanceType.JOINED)
public class Person{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected Long id;
//name,birthdate ...
}
#Entity
#Table(name = "person_details")
#PrimaryKeyJoinColumn(name="id")
public class PersonDetails extends Person{
// private details accessible only for authorized user
}
When I create a Person I can't associate a PersonDetails it creates automatically a new instance of PersonDetails and add a new line in Person table.
Here's my repositories.
#NoRepositoryBean
public interface PersonBaseRepository<T extends Person> extends JpaRepository<T, Long> {
T findByNameAndFirstname(String name,String firstname);
}
#Transactional
public interface PersonRepository extends PersonBaseRepository<Person> {
}
#Transactional
public interface PersonDetailsRepository extends PersonBaseRepository<PersonDetails > {
}
To solve this I could instanciate my Entities only with PersonDetails but in some cases PersonDetails fields will be empty.
When I call findByNameAndFirstname from PersonDetailsRepository and the person isn't in person_details table but only in person I want to return the person matches in PersonDetails object.
Does anyone have a workable solution ? Thanks for your help.

Spring Data JPA remove child entities

I have a load repository.
#Transactional
public interface MyLoadRepository extends CrudRepository<ParentEntity, Serializable> {
}
Then is my ParentEntity.
#MappedSuperclass
public class ParentEntity {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "id", unique = true)
private String uuid;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
}
Then I have multiple child entities.
#Entity
#Table(name = "EntityA")
public class EntityA extends ParentEntity {
}
#Entity
#Table(name = "EntityB")
public class EntityB extends ParentEntity {
}
Ques : I want to delete these entities separately by my repository.
If I write something like this.
#Autowired
private MyLoadRepository repository;
and then repository.deleteAll()
I get error that repository is not entity (It obiviously not).
Here I want to delete either entityA or entityB data completely based on some condition. How can I do that ?
We should create repository per entity and not on non entity classes.
So, for your case you need 2 repository classes
#Transactional
public interface EntityARepo extends CrudRepository< EntityA, String> {
}
#Transactional
public interface EntityBRepo extends CrudRepository< EntityB, String> {
}
now in service classes you can do
#Autowired
private EntityARepo repoA;
#Autowired
private EntityBRepo repoB;
and then you can call delete method based on your condition
repoA.deleteAll()
or
repoB.deleteAll()
You need to fetch the entity based on a condition. For example, if the EntityA has a primary key uuid, then you must find EntityA by uuid and then delete the EntityA.
EntityA entityA = entityARepo.findOne(uuid);
repository.delete(entityA);
EntityB entityB = entityBRepo.findOne(uuid);
repository.delete(entityB);

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)

Change default sort order for Spring Data findAll() method

I'm using Spring Data JPA and I wonder if it is possible to change the default sort order for a entity being used by the Spring Data findAll() method?
You can achieve this as follows:
dao.findAll(new Sort(Sort.Direction.DESC, "colName"));
// or
dao.findAll(Sort.by("colName").descending());
Another way to achieve the same. Use the below method name:
findByOrderByIdAsc()
You should be able to do this by either:
in spring-data 1.5+, overriding the findAll() method in your Interface, adding the #Query annotation and creating a named Query in your Entity class like, for example, below:
Entity
#Entity
#NamedQuery(name = "User.findAll", query="select u from User u order by u.address.town")
public class User{
}
Repository
public interface UserRepository extends ... <User, Long> {
#Override
#Query
public Iterable<User> findAll();
}
or,
by creating a custom repository implementation:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
Use a PagingAndSortingRepository instead.
With that in place you can add a queryparameter ?sort=,
Repository:
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
//no custom code needed
}
GET Request:
localhost:8080/users?sort=name,desc
If you want to add costom query to findAll() jpa query you can do it this way
here i changed my default order
According to my default order is primary key it is id
but now i here set id_order to change my default order
Model class
#Entity
#Table(name = "category")
#NamedQuery(name = "Category.findAll", query="select u from Category u order by
u.id_order")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String nameEn;
private String nameSi;
private String nameTa;
private Integer id_order;
Repository class
import com.model.Category;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface CategoryRepository extends CrudRepository<Category, Integer> {
#Override
#Query
public Iterable<Category> findAll();

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