Mapping AbstractEntity to SubClassDTO - Single Table Strategy - java

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.

Related

How to map an abstract class without an id in Hibernate

The background of this project is that there are two types of users, suscriber users and amdin users. There are also two types of sucriber users, students and professors. Admin users can register new classrooms, and suscriber users can suscribe to classrooms to see different information such as temperature etc.
The problem is that I have to map a ManyToMany bidirectional relationship between clasrooms and suscriber users and I'm getting the following error in the Classroom class:
'Many To Many' attribute value type should not be 'SuscriberUser'
and this exception:
org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class
This is my code:
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class User implements IUser {
private String name;
// some other fields
// constructors
// getters and setters
}
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class SuscriberUser extends User implements ISuscriberUser {
#ManyToMany(mappedBy = "suscribers")
private ArrayList<Classroom> classroomSubscriptions;
// constructors
// getters and setters
}
For example one concrete class of SuscriberUser:
#Entity
#Table(name = "student")
public class Student extends SuscriberUser {
#Id
private int studentId;
// constructors
// getters and setters
}
#Entity
#Table(name = "classroom")
public class Classroom implements IClassroom {
#Id
private int internalId;
// other fields
#ManyToMany()
#JoinTable(name = "suscribers")
private ArrayList <SuscriberUser> suscribers;
// constructors
// getters and setters
}
I have also tried using #MappedSuperclass in both classes User and SuscriberUser, but it doesn't work. I guess it's because both abstract classes don't have an id.
How can I solve this?
The User class is just a collector of fields, therefore it can become a #MappedSuperClass.
#MappedSuperClass
public abstract class User implements IUser {
private String name;
// some other fields
// constructors
// getters and setters
}
If you use #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS), you have a table per class, so you need to:
remove abstract
add #Entity annotation
add #Table to define a name
add #Id to the id column.
#Entity
#Table(name = "subscriber_user")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class SuscriberUser extends User implements ISuscriberUser {
#Id
private int id;
#ManyToMany(mappedBy = "suscribers")
private List<Classroom> classroomSubscriptions;
// constructors
// getters and setters
}
More info here.
The student class does not need an id column because is in the parent class.
#Entity
#Table(name = "student")
public class Student extends SuscriberUser {
// constructors
// getters and setters
}
Pay attention that the join table is another one. This table contains the relations between students and classrooms and it can be named subscription.
#Entity
#Table(name = "classroom")
public class Classroom implements IClassroom {
#Id
private int internalId;
// other fields
#ManyToMany
#JoinTable(
name = "subscription",
joinColumns = #JoinColumn(name = "internalId"), // id class room
inverseJoinColumns = #JoinColumn(name = "id")) // id user
private List<SuscriberUser> suscribers;
// constructors
// getters and setters
}

How can I override a javax.persistence.AttributeConverter

I have the problem that want to override an AttributeConverter in a subclass of an entity, but the defined converter in the subclass does not get called. According to the AttributeConverter documentation this should be the way, but it does not work for me. What am I doing wrong?
#Entity
#org.hibernate.annotations.DynamicUpdate(value = true)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("ORDER")
public class Order implements Serializable
{
...
#Column(name = "PRODUCT_SERIALIZED", updatable = false)
#Convert(converter = ProductConverter.class)
protected Product product;
...
}
#Entity
#DiscriminatorValue("CUSTOMER_ORDER")
#Convert(attributeName = "product", converter = CustomerProductConverter.class)
public class CustomerOrder extends Order
{
...
As it seems #Convert is not suitable to override an existing converter for a field of a superclass. I solved it in a different way. I injected a conversionService via CDI into the AttributeConverter of the superclass which I then can specialized.
#Converter
public class ProductConverter implements AttributeConverter<Product, String>
{
ProductConverterService converterBean = null;
#Override
public String convertToDatabaseColumn(Product attribute)
{
return getConverterService().convertToDatabaseColumn(attribute);
}
#Override
public Product convertToEntityAttribute(String dbData)
{
return getConverterService().convertToEntityAttribute(dbData);
}
public ProductConverterService getConverterService()
{
if (converterBean == null)
{
//since ProductConverter is obiously not managed via CDI
converterBean = CDI.current().select(ProductConverterService.class).get();
}
return converterBean;
}
}

Initialise immutable JPA collection in database

My domain contains FieldValidator objects, that have sub classes EmailValidator, SizeValidator etc. There is only one instance of FieldValidator per subclass.
#Entity
#Immutable
#Table(name = "FIELD_VALIDATOR")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE")
public abstract class FieldValidator extends AbstractEntity implements IFieldValidator{
#Column(unique=true, name="TYPE")
public String type;
public String name;
public String argumentName;
//To override by subclasses
public abstract Optional<CustomError> run(FieldConstraint constraint);
/**
* Constructor, getters and setters omitted
*/
}
Example subclass
#Entity
#DiscriminatorValue("DATE_BEFORE")
public class DateBeforeValidator extends FieldValidator {
public DateBeforeValidator() {
super("Date", "Before");
}
public Optional<CustomError> run(FieldConstraint constraint) {
//TODO
}
}
Example subclass
#Entity
#DiscriminatorValue("SIZE_MIN")
public class SizeMinValidator extends FieldValidator {
public SizeMinValidator() {
super("Size", "Minimum");
}
public Optional<CustomError> run(FieldConstraint constraint) {
//TODO
}
}
How can I initialise the FIELD_VALIDATOR table with one entity per FieldValidator subclass? I need ensure that: the FIELD_VALIDATOR table is immutable (no other data can be added or deleted to/from the table), each FieldValidator entity is unique and immutable (the #Column(unique=true) and the #Immutable annotations should do the trick).
I think your best bet is JPAs/Hibernates initial load feature. You can read about it here for example.

QueryDSL query exception

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.

Mapping inherited fields

I'm using EJB 3 with Hibernate 4 on Weblogic 10.3.4.
I have a problem when I try to select an entity based on a property that doesn't exists in its parent entity:
I have in my system something like this: A basic abstract VehicleDefinition class and two concrete subclasses - CarDefinition and TruckDefinition.
Also, I have a basic abstract VehicleInstance and two concrete subclasses - CarInstance and TruckInstance.
In VehicleInstance I have a VehicleDefinition field.
In the TruckDefinition class I map a field (Height) that doesn't exists In VehicleDefinition or in CarDefinition. The problem is that with the mentioned mapping, I can't use an HQL to select a TruckInstance by a the field of TruckDefintion (i.e. A TruckInstance by height), because TruckInstance only "knows" VehicleDefinition, not TruckDefinition.
The mappings goes something like this:
#Entity
#Table(name = "VEHICLES" uniqueConstraints = {#UniqueConstraint(columnNames = {"TYPE_NAME"}) })
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "VEHICLE_TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class VehicleDefinition {...}
#Entity
#DiscriminatorValue("222")
public class CarDefenition extends VehicleDefinition {...}
#Entity
#DiscriminatorValue("555")
public class TruckDefenition extends VehicleDefinition{
...
private Integer mHeight;
#Column(name = "TRUCK_HIGHT")
public Integer getHeight()
{
return mHeight;
}
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorFormula("CASE WHEN 222 = (SELECT a.VEHICLE_TYPE FROM VEHICLES a WHERE a.id = VEHICLE_ID) THEN 222 ELSE 555 END")
#Table(name = "VEHICLE_INSTANCES" uniqueConstraints = {#UniqueConstraint(columnNames = {"VEHICLE_ID", "LPLATE"}) })
public abstract class VehicleInstance {
...
private VehicleDefinition mDefinition
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "VEHICLE_ID" referencedColumnName = "ID", nullable = false)
public VehicleDefenition getDefinition()
{
return mDefinition;
}
}
#Entity
#DiscriminatorValue("222")
public class CarInstance extends VehicleInstance {...}
#Entity
#DiscriminatorValue("555")
public class TruckInstance extends VehicleInstance {...}
I tried to refactore he classes, making getDefinition in VehicleInstance abstract, and having TruckInstance and CarInstance implement, both returning their repectivetypes, but then I couldn't figure out how two map the new methods. Should I keep the original annotations at VehicleInstance? Should both of them point to VEHICLE_ID? Every combination I tried seemed to fail.
So in conclusion - How can I map VehicleInstance, CarInstance and TruckInstance so that I can select in HQL a TruckInstance based on its Height property?
Thanks in advance!

Categories

Resources