I have a few tables named MEMBER, PROVIDER, EMPLOYER etc and they have few common columns like ID, ADDRESS etc. I'm using hibernate to fetch data from these tables using Spring Data JPA. Since I have many similar tables, I created a super entity class and wrote a common method to fetch the data. Here is my super class.
#Entity
#Getter
#Setter
#NoArgsConstructor
#ToString
public abstract class Recipient implements Serializable {
#Id
#Column(name = "RECIPIENT_ID", unique = true, nullable = false)
private Long id;
}
Here is a sample entity which extends this super class.
#Entity
#Getter
#Setter
#NoArgsConstructor
#ToString
#Table(name = "MEMBER")
public class Member extends Recipient {
#Column(name = "Member_NAME")
private String membername;
}
Here is the Service
public class RecipientServiceImpl extends AbstractService<Recipient>
implements RecipientService {
#Override
public Recipient findByIdAndType(long id, String type) {
Recipient recipient = null;
switch (RecipientType.get(type)) {
case MEMBER:
recipient = recipientRepository.findMemberById(id);
case PROVIDER:
recipient = recipientRepository.findProviderById(id);
case EMPLOYER:
recipient = recipientRepository.findEmployerById(id);
}
}
And here is the repository
public interface RecipientRepository
extends JpaRepository<Recipient, Long>, JpaSpecificationExecutor<Recipient> {
public Member findMemberById(long id);
public Provider findProviderById(long id);
public Employer findEmployerById(long id);
}
Obviously it didn't work since there weren't any tables mapped in the Recipient abstract entity. Is there a way to implement what I was hoping for as a generic fetching method instead of having to create repositories for each entities ?
Related
I'm trying to mapp Entity (TrainingEntity) to DTO, where one of the fields is a Set with ManyToMany reference to an AbstractEntity (CoachEntity) divided by Single Table into two subclasses: ExternalCoach and InternalCoach.
Both subclasses have different data, therefore require two different mappers.
#Entity
#Table(name = "TRAINING")
public class TrainingEntity extends AbstractEntity {
#ManyToMany()
#JoinTable(name = "TRAINING_EMPLOYEE", joinColumns = { #JoinColumn(name = "TRAINING_ID") }, inverseJoinColumns = {
#JoinColumn(name = "COACH_ID") })
private Set<CoachEntity> coachEntities;
#Column(nullable = false)
private TrainingType trainingType;
......some data....
}
Abstract Coach Entity
#Entity
#Table(name = "COACH")
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class CoachEntity extends AbstractEntity {
......some data....
}
Coach Subclasses:
#Entity
#DiscriminatorValue("EXTERNAL")
public class ExternalCoachEntity extends CoachEntity {
......some data....
}
#Entity
#DiscriminatorValue("INTERNAL")
public class InternalCoachEntity extends CoachEntity {
......some data....
}
One mapper for Abstract Coach class won't have acces to subclasses methods and fields, so I need two different for External and Internal. Than I would have to use them in TrainingMapper class, but (example of internal):
public class CustomTrainingMapper {
public static TrainingDto toTrainingDto(TrainingEntity trainingEntity){
if(trainingEntity == null){
return null;
}
if(trainingEntity.getTrainingType().equals(TrainingType.INTERNAL)){
Set<CoachEntity> coachEntities = trainingEntity.getCoachEntities();
Set<CoachDto> coachDtos = CustomInternalCoachMapper.toDTOSet((Set<InternalCoachEntity>)coachEntities);
}
I get:
cannot cast from Set<CoachEntity> to Set<InternalCoachEntity>
Without cast it simply doesn't see mappers methods with subclass input.
The method toDTOSet(Set<InternalCoachEntity>) in the type CustomInternalCoachMapper is not applicable for the arguments (Set<CoachEntity>)
When in mapper I change method input to abstract Coach Class it doesn't see subclasses methods and fields.
Part of InternalMapper:
public class CustomInternalCoachMapper {
public static CoachDto toCoachDto(InternalCoachEntity coachEntity) {
if (coachEntity == null) {
return null;
}
EmployeeDto employeeDto = CustomEmployeeMapper.toEmployeeDto(coachEntity.getEmployeeEntity());
return new InternalCoachDto(coachEntity.getId(), coachEntity.getVersion(), coachEntity.getCreateDate(),
coachEntity.getUpdateDate(), coachEntity.getName(), coachEntity.getSurname(), employeeDto);
}
Is it possible to mapp this AbstractEntity Set into subclasses DTOs?
I also tried with AbstractDto for Coaches, but then I'm facing the same problem with no access to subclasses getters and setters.
I am using JPA query in spring , My subclass extends Baseclass whic contains an Id only and My subclass has all the variavles that is used by the JPA query given below:
Base Class:
#MappedSuperclass
#Table(name = "partcost")
#NoArgsConstructor
#AllArgsConstructor
#Data
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Pg6p0012_01PartCostBaseQueryModel implements Serializable {
private static final long serialVersionUID = 1L;
#Id
String part_no;
}
Subclass :
#Entity
#Table(name = "partcost")
#NoArgsConstructor
#AllArgsConstructor
#Data
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Pg6p0012_01PartCost1QueryModel extends Pg6p0012_01PartCostBaseQueryModel implements Serializable {
private static final long serialVersionUID = 1L;
private String stock_take_cost ;
private String cost_type ;
}
when I am hiting below JPA Query :
#Repository
#Transactional
public interface Pg6p0012_01PartcostRepository extends JpaRepository<Pg6p0012_01PartCostBaseQueryModel, String> {
#Query(value = "SELECT stock_take_cost,cost_type FROM partcost where part_no = :p_part_no", nativeQuery = true)
public List<Pg6p0012_01PartCost1QueryModel>getPartcost1Result(#Param("p_part_no") String p_part_no);
}
its throwing Error: No such column name
which is clear because query is returning only one column but Model has two columns .
How to tackle this ? please suggest.
You make part_no transient . It means that it is not persisted in the database.Therefore you are getting no such column name error. Remove #Transient from the base class which is above the part_no.
And also annotate your base class with
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
Hibernate supports the three basic inheritance mapping strategies:
table per class hierarchy
table per subclass
table per concrete class
I got this error:
org.springframework.data.mapping.model.MappingException: Attempt to
add id property private java.lang.String
com.example.estrans.domain.entities.bookshop.Entity.id but already
have property private java.lang.String
com.example.estrans.domain.entities.bookshop.Book.bookId registered as
id. Check your mapping configuration!
When trying to use spring-data the Book entity that inherits form Entity. The Entity tries to cover all of the common functionalities from all potential specific entities.
Entity
#Getter
public class Entity {
#Id
private String id;
public IndexQuery getIndexQuery(){
return new IndexQueryBuilder().withId(this.getId()).withObject(this).build();
}
}
Book
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Document(indexName = "bookshop", type = "book", shards = 1, replicas = 0, refreshInterval = "-1")
public class Book extends Entity {
#Id
private String bookId;
#Field(type = FieldType.String, store = true)
private String title;
}
How can I handle the inheritance while persiting only the concrete Book entity not the general Entity?
EDIT
Take a look at the discusion with #builder inheritance support https://github.com/rzwitserloot/lombok/issues/853
I'm trying to map a class hierarchy to a single table using Hibernate and one table not creating. I add #Inheritance(strategy=InheritanceType.SINGLE_TABLE) but in base there are separate tables.
#MappedSuperclass
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class User implements UserDetails {
#Id #GeneratedValue
private int id;
...
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
...
}
#Entity
public class Manager extends User{
...
}
#Entity
public class Administrator extends User{
...
}
Whats wrong?
Your User class should be
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(field = "type", discriminatorType = DiscriminatorType.STRING)
public class User implements UserDetails {
/* your original stuff */
}
Then you need to map the extended classes as follows:
#Entity
#DiscriminatorValue("manager")
public class Manager extends User { /* stuff */ }
#Entity
#DiscriminatorValue("administrator")
public class Administrator extends User { /* stuff */ }
This effectively should create a single table which houses all the fields from User, Manager, and Administrator using a special field that is added by Hibernate called type which will hold values of either manager or administrator.
I have a problem with a QueryDSL query. Classes:
#Entity
#Table(name="project")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Project extends DomainObject implements Comparable<Project>, IconizedComponent, Commentable {
#ManyToMany(targetEntity=Student.class)
#JoinTable(name="project_student")
#Sort(type=SortType.NATURAL) //Required by hibernate
#QueryInit({"user"})
private SortedSet<Student> projectParticipants = new TreeSet<Student>();
private Project(){}
//attributes, get+set methods etc
}
#Entity
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class Student extends Role {
public Student(){}
//attributes, get+set methods etc
}
#Entity
#DiscriminatorColumn(name = "rolename", discriminatorType = DiscriminatorType.STRING, length = 8)
#Table(name="role", uniqueConstraints={#UniqueConstraint(columnNames={"user_id","rolename"}, name = "role_is_unique")})
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Role extends LazyDeletableDomainObject implements Comparable<Role> {
#ManyToOne(optional=false)
protected User user;
public Role(){}
//attributes, get+set methods etc
}
#Entity
#Table(name="user")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class User extends LazyDeletableDomainObject implements Comparable<User>, IconizedComponent {
private String firstName;
private String lastName;
public User(){}
//attributes, get+set methods etc
}
Query:
private BooleanExpression authorsNameContains(String searchTerm){
QUser user = new QUser("user");
user.firstName.containsIgnoreCase(searchTerm).or(user.lastName.contains(searchTerm));
QStudent student = new QStudent("student");
student.user.eq(user);
return QProject.project.projectParticipants.contains(student);
//java.lang.IllegalArgumentException: Undeclared path 'student'. Add this path as a source to the query to be able to reference it.
}
I have also tried annotating the projectParticipants set in Project with
#QueryInit("*.*")
But that gives the same exception. Any hints?
#Timo Westkämper
#siebZ0r
Thanks for your attention. Sorry for the delayed reply and incorrectly phrased question. Actually what I wanted to do was to write a working BooleanExpression.
In combination with the annotations already made, this was what I was after:
private BooleanExpression authorsFirstNameContains(String searchTerm){
return QProject.project.projectParticipants.any().user.firstName.containsIgnoreCase(searchTerm);
}
I got this right with the help of a colleague.