Query to find Parent entity by Child attribute where Parent also have same attritube, in spring data jpa - spring-data-jpa

I am trying to create a query in Spring-data-jpa to find a Person entity by Address's id. Person have OneToOne relationship with Address and both have id as primary key.
public abstract class AbstractEntity{
#Id
Long id;
}
public class Person extends AbstractEntity {
#OneToOne
Address address;
}
public class Address extends AbstractEntity {
}
public interface PersonRepository implements JpaRepository<Person, Long> {
Person findByAddressId(Long addressId); // Throws cannot create metamodel exception
Person findByAddress_Id(Long addressId); // Throws cannot create metamodel exception
}
I'm thinking that the method name findByAddressId and findByAddress_Id are ambiguous for Spring data jpa query look-up strategies as both Person and Address entities have id as their attributes.
Is it possible to write query in Spring-data-jpa to find parent entity by child attribute where parent and child both have same attribute, without writing SQL?

Related

How can I use JPA Query Methods to return an OR condition with NULL?

Am trying to create a Query that either matches all rows that equal tier or are NULL. Using Query Methods as described in Spring JPA Docs. The Default implementation below works if I just pass in the tier:-
#Entity
#Table(name = "tier")
class UuTier {
Long id;
Long tierId;
}
#Entity
#Table(name = "user")
class User {
#OneToOne
#JoinColumn(name="tier_id")
UuTier uuTier;
// Other Relationships
}
public interface UserRepository extends Repository<User, Long> {
List<User> findByTier_Id(#Param("tier")Long tier);
}
What I need is something like this, which is throwing an error " No property null found for type User". Can I achieve this ask using Query Methods?:-
public interface UserRepository extends Repository<User, Long> {
List<User> findByTierOrNull_Id(#Param("tier")String tier);
}
Following up from one of the responders (who for some reason deleted her post) - I got this to work!!
#Query("SELECT entity FROM User entity LEFT JOIN UuTier uuTier ON entity.uuTier.tier = uuTier.tier"
+ " WHERE entity.uuTier.tier = :tier OR entity.uuTier.tier IS NULL")
public List<User> findByTierOrNull_Id(#Param("tier") Long tier);

Transactional not working with JPA saveAll

I'm trying to save a list of entity objects. Here is my method which is defined in a Service class:
#Transactional
public void creteEmployee(List<Employee> employeeList) {
employeeRepository.saveAll(employeeList);
}
And here is my JPA repository:
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
Page<Employee> findByCompany(Pageable pageable, Company company);
}
I've a unique constraint defined in my entity. If any item from the employeeList violates the unique constraint, I expect no one from the employeeList will be saved. Unfortunately all items except the problematic one is saved and also an DataIntegrityViolationException is raised. What should be done to make it atomic?

JPA EntityManager finder method returning other sub class object

I am facing an issue with JPA EntityManager finder method. JPA entities are using inheritance structure as follows:
//Need to persist this table to database
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Table(name="Table1")
public class BaseEntity implements Serializable {
#Id
#Column(name="PRIMARY_ID")
private long id;
private String field1;
.......
}
//This table will NOT persist and has parameters only for Sub classs
#MappedSuperclass
public abstract class MappedSuperClassEntity extends BaseEntity {
private String field2;
private String field3;
........
}
//This sub class is persisted and inherits fields form above class including Primary Key using TABLE_PER_CLASS strategy defined in BaseEntity
#Entity
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Table(name="SubTable1")
public class Sub1 extends MappedSuperClassEntity {
private String field4;
private String field5;
...............
}
//This sub class is persisted and inherits fields form above class including Primary Key using TABLE_PER_CLASS strategy defined in BaseEntity
#Entity
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Table(name="SubTable2")
public class Sub2 extends MappedSuperClassEntity {
private String field4;
private String field5;
..............
}
As you can see, Sub1 and Sub2 are persistable entites and both of which extends MappedSuperClassEntity which is annotated with `#MappedSuperClass'. This class further inherits BaseEntity which has decribed TABLE_PER_CLASS inheritance strategy.
I enabled the hibernate stat collector and found that hibernate is storing sub class objects using key of Parent class. So in the above case it stores the data as follows for Sub1 finder in cache:
14:17:03,941 DEBUG [org.hibernate.cache.TransactionalCache] cache lookup: com.abc.BaseEntity#10
14:17:03,943 DEBUG [org.hibernate.cache.TransactionalCache] cache miss
14:17:03,948 DEBUG [org.hibernate.cache.TransactionalCache] caching: com.abc.BaseEntity#10
Next time if i do a find for Sub2 for same id (10), hibernate thinks it is in Cache as it uses Parent Class as key and returns the Sub1 object as follows:
14:27:54,574 DEBUG [org.hibernate.cache.TransactionalCache] cache lookup: com.abc.BaseEntity#10
14:27:54,575 DEBUG [org.hibernate.cache.TransactionalCache] cache hit
So this is happening when you run the finders for Sub1 and Sub2:
entityManager.find(Sub1.class, id); //returns Sub1 object
entityManager.find(Sub2.class, id); //returns Sub1 object (PROBLEM HERE).
Please help me to fix the issue (I do not want to clear cache in between these calls)
The problem is that you're using a base entity when that doesn't make sense. When you inherit from a base entity, and not just from a mapped superclass, you're not just inheriting fields and methods. You're establishing an is a relationship.
An example where that would make sense is the following: Car and Bike both inherit a base entity Vehicle. In that case, a Car entity is a Vehicle entity. And a Bike entity is a Vehicle entity.
If a car has an ID 42, then a Bike may not also have the ID 42, because you would have two vehicles with the same ID. Imagine a Driver entity with a ManyToOne association with a vehicle (i.e. a driver drives a vehicle). If I store the ID 42 in the vehicle_id column of the driver table, this ID 42 must uniquely identify a vehicle. It could be a car, it could be a bike, and hibernate will look in both tables, but it can't be both at the same time.
You're violating this inheritance concept. BaseEntity should not be annotated with Entity. It should just be a MappedSuperclass, which just allows inheriting fields and methods, but doesn't establish this semantic is a association.

how to filter out entity object inside entity in rest api

I am using Spring Boot to implement rest api. There are three entities SeqTb, PairTb, and GroupTb and they are nested. SeqTb has manytoone with PairTb. PairTb has onetomany relationship with SeqTb and also manytoone with GroupTb.
//SeqTb.java
#Entity
#Table(name="SEQ_TB")
public class SeqTb implements Serializable {
.......
#ManyToOne
#JoinColumn(name="PAIR_ID")
private PairTb pairTb;
......
}
// PairTb.java
#Entity
#Table(name="PAIR_TB")
#NamedQuery(name="PairTb.findAll", query="SELECT p FROM PairTb p")
public class PairTb implements Serializable {
#ManyToOne
#JoinColumn(name="GROUP_ID")
private GroupTb groupTb;
#OneToMany(mappedBy="pairTb", cascade=CascadeType.ALL)
private List<SeqTb> seqTbs;
}
//GroupId.java
#Entity
#Table(name="GROUP_TB")
public class GroupTb implements Serializable {
//bi-directional many-to-one association to PairTb
#OneToMany(mappedBy="groupTb", cascade=CascadeType.ALL)
private List<PairTb> pairTbs;
}
In my controller GET request with analysisId was handled in the following way:
#RequestMapping(
value = "/api/seqs/{analysis_id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SeqTb> getSeqByAnalysisId(#PathVariable("analysis_id") String analysis_id) {
SeqTb seq = seqService.findByAnalysisId(analysis_id);
return new ResponseEntity(seq, HttpStatus.OK);
}
I also create a bean class SeqServiceBean that extends the interface SeqService which in turn calls methods from the following JPA repository for query.
//SeqRepository.java
#Repository
public interface SeqRepository extends JpaRepository<SeqTb, Integer> {
#Override
public List<SeqTb> findAll();
public List<SeqTb> findByAnalysisId(String analysisId);
}
When I query a SeqTb object with SeqTb.PairTb == null, the api works just fine. However, if the analysisId I put in the url belongs to a SeqTb record that associates with a pairId which in turn belongs to a groupId, the program would go nuts. Below is the output, the first part output is correct (bold text). After that it keeps printing PairTb and GroupTb in loops (repeating keywords pairTb, groupTb).
{"rowId":8,"analysisId":"cce8d2c2-a6dc-4ee9-ba97-768f058abb50","analyteCode":"D","center":"UCSC",
"pairTb":{"rowId":4,"pairCode":"01ad975d-c2ed-4e4d-bd3b-c9512fc9073c","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":[{"rowId":1,"pairCode":"00ad0ffe-2105-4829-a495-1c2aceb5bb31","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":
Meanwhile I got lots of errors from tomcat server:
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:565) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
How do I ignore the nested entity object inside an entity and get only the meaning columns?
You can also annotate a property with #JsonIgnore in order to not output that field.
Found the solution. Created a value object that only contains the specific columns from entity and leave out the nested entity object. And it works.

How do I represent this using JPA?

I would like a 'RolesPlayed' entity with the following columns
user
role
department/project/group
All the three columns above constitute a composite primary key. I would like to know if defining a column to be one of department/project/group possible ? If yes, how ? Or do I need to break the entity into DepartmentRoles, GroupRoles and ProjectRoles.
Thanks.
You could use polymorphism with an abstract base class to do that.
#Entity
public class RolePlayed {
#ManyToOne
private User user;
#ManyToOne
private Role role;
#ManyToOne
private Body body;
...
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Body {
...
}
#Entity
public class Department extends Body {
...
}
#Entity
public class Project extends Body {
...
}
#Entity
public class Group extends Body {
...
}
Check out the Polymorphism section in the Java Enterprise tutorial for a good overview.
Alternatively, you could also make the RolePlayed entity abstract, with DepartmentRole, GroupRole and ProjectRole implementations.