4

We have 2 tables (an active table and an archive table) which have the same structure (ex. Employee and EmployeeArchive). To be able to leverage common code to use results for both tables we have an abstract parent class that defines all the methods and annotations. We like to be able to perform queries that will use the same query for both tables and union the results together.

We have another entity/table (ex. Organization) with a onetomany/manytoone bidirectional relationship with Employee; Organization has a List of Employees and every employee has an organization. When getting the employees of an organization via the association we only want the employees from the active table not the archive.

Is there a way to achieve what we are attempting or a viable workaround?

We have tried various implementations of @MappedSuperclass, @Entity/@InheritanceType.TABLE_PER_CLASS to try to achieve what we want. Each implementation would nearly achieve what we want but not completely. For example to be able to query both tables we could have an abstract parent Entity with InheritanceType.TABLE_PER_CLASS but then we could not have the mappedBy relationship to Employee in the Organization. We can use a MappedSuperclass as the parent to be able to have the correct relationship but then we cannot query both the Archive and Active tables via the union.

Here is basically what we are trying to layout:

    @Entity
    @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
    public abstract class AbstractEmployee {
        @ManyToOne
        @JoinColumn(name="employeeId", nullable=false)
        Organization org;
        ...
    }

    @Entity
    public class Employee extends AbstractEmployee {
    }

    @Entity
    public class EmployeeArchive extends AbstractEmployee {
    }

    @Entity
    public class Organization {
       @OneToMany(cascade=ALL, mappedBy="org")
       List<Employee> employees;         
       ...            
    }

Code

    public List<AbstractEmployee> getAllEmployees()
    {
       Query query = em.createQuery("SELECT e FROM AbstractEmployee e where e.name = ‘John’", AbstractEmployee.class);
       return query.getResultList();
    }

    public List<Organization> getOrganizations()
    {
       Query query = em.createQuery("SELECT e FROM Organization o ", Organization.class);
       List<Organization> orgs = query.getResultList();
       // fetch or eager fetch the Employees but only get the ones from the active employee table
       return orgs;
    }

We also tried to have the parent class extend the MappedSuperclass and put the implementation and annotations in the MappedSuperclass but we get an AnnotationException for the relationship of the Organization

    @MappedSuperclass
    public abstract class AbstractMapped {
        @ManyToOne
        @JoinColumn(name="employeeId", nullable=false)
        Organization org;
    }

    @Entity
    @Inheritance(@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS))
    public abstract class AbstractEmployee extends AbstractMapped {
        ... `Constructors` ...
    }

On deployment we get the following exception:

    Caused by org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: Employee.org in Organizaztion.employees
       at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:685)
Community
  • 1
  • 1
bdrx
  • 924
  • 13
  • 31
  • I have a similar problem. Are you willing to help me with it? Here is the link: http://stackoverflow.com/questions/25252541/generatedvalue-for-a-java-abstract-superclass-over-mysql – CodeMed Aug 11 '14 at 22:49

3 Answers3

2

You can do this by changing the mapping of Organization to Employee so that it uses a relationship table, rather than having the org field in the Employee table. See the example in the Hibernate documentation, which for you would look something like:

@Entity
public class Organization {
   @OneToMany(cascade=ALL)
   @JoinTable(
        name="ACTIVE_EMPLOYEES",
        joinColumns = @JoinColumn( name="ORGANIZATION_ID"),
        inverseJoinColumns = @JoinColumn( name="EMPLOYEE_ID")
   )
   List<Employee> employees;         
   ...            
}

However, I have to say that I think having two tables to represent current vs archived Employees is a bad idea. This sounds to me like a 'soft delete' kind of situation, which is better handled with an in-table flag (IS_ACTIVE, or something). Then you don't have these odd abstract classes to do your queries, multiple tables with the same kind of data, etc etc. A bit of a description of this strategy is here.

Then you can use the non-join table mapping that you've already got, and use the @Where annotation to limit the employees in an organization to ones that have IS_ACTIVE set to true. An example of this approach is here.

Community
  • 1
  • 1
sharakan
  • 6,821
  • 1
  • 34
  • 61
  • your solution is much more sane! I like it +1 – Mihai Soloi Jun 24 '13 at 19:23
  • Thanks sharakan, the jointable mapping seems like it will work (have not tried it yet). The database strategy is slightly out of my control, but I will have some discussions to see if we can possibly use a different 'soft delete' strategy such as just having an archive flag or putting all the data into the archive table/db so that we do not every need to worry about querying both. – bdrx Jun 25 '13 at 15:47
1

This is one of the annoying things about hibernate. The way to do this is to have another abstract class, AbstractMapped, which simply looks like this:

@MappedSuperclass
public abstract class AbstractMapped {

}

and then have AbstractEmployee extend AbstractMapped. Then you have AbstractEmployee as both an Entity and a Mapped Superclass, even though the two tags are mutually exclusive.

Martin Wickham
  • 442
  • 4
  • 9
  • I should have mentioned in the post that we had already attempt that to no avail. We get an exception initializing the persistence unit. I will update the question. – bdrx Jun 24 '13 at 15:04
0

AbstractEmployee should be the @MappedSuperClass, and should not be an @Entity, which creates a table for the class.

Organization should contain a List<AbstractEmployee> not of Employee.

Mihai Soloi
  • 373
  • 1
  • 12
  • If Organization contains a `List` then the relationship retrieval will query both tables, the archive and the active, which is what we are trying to avoid. We do not want the query to get the employees of Organization to query the archive table. – bdrx Jun 25 '13 at 15:52