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)
4

3 回答 3

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.

于 2013-06-24T19:08:53.853 回答
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.

于 2013-06-23T06:10:00.923 回答
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.

于 2013-06-24T18:42:24.580 回答