简体   繁体   中英

Confused with Spring Data JPA and generic types

Tables:

StudentHistory 1--->n Student
TeacherHistory 1--->n Teacher

I try to regroup the JPA behaviour of History because they do the same thing (retrieve the students/teacher from a given history for example).

Entities with generic types:

// Entities
public abstract class AbstractHistory <T> {}
public class StudentHistory extends AbstractHistory<Student> {}
public class TeacherHistory extends AbstractHistory<Teacher> {}

Repositories with genric types:

// repositories
public interface IHistoryRepository<T> extends CrudRepository<AbstractHistory<T>, Long> {
    public AbstractHistory<T> findFirst();
}    
public interface StudentHistoryRepository extends IHistoryRepository<Student> {}
public interface TeacherHistoryRepository extends IHistoryRepository<Teacher> {}

I though I could do:

StudentHistory stuHisto = new StudentHistoryRepository().findFirst(); 

But I get this error:

    // err ->  Type mismatch: cannot convert from AbstractHistory<Student> to StudentHistory

1/ Why can't I retrieve a 'StudentHistory' from my 'StudentHistoryRepository' ?

2/ How should I deal whith that?

You have this problem because your method explicitly returns an AbstractHistory and not the subtype.

  1. You would need to cast...
  2. ... if only your repository implementation understood that each T you get a specific history.

You may try adding another type but I fear that it'll fail:

public interface IHistoryRepository<
  T,
  H extends AbstractHistory<T>
> extends CrudRepository<H, Long> {
    public H findFirst();
}    
public interface StudentHistoryRepository extends IHistoryRepository<Student, StudentHistory> {}
public interface TeacherHistoryRepository extends IHistoryRepository<Teacher, TeacherHistory> {}

I don't know what framework you are using, probably Spring Data from the names; while I had used it in the past, I don't know if it is able to do that.

After all, it needs to get the concrete class and since it is generics, type erasure may interfere (if the information about the concrete type representing H is lost in reflection then Spring Data won't probably be able to do much here, unless you help it with an annotation or something else).

Another solution that should work is to do that per each child interface instead:

public interface StudentHistoryRepository extends CrudRepository<StudentHistory, Long> {
  StudentHistory findFirst();
}

Or with another interface:

  public interface FindFirst<T> {
    T findFirst();
  }

  public interface StudentHistoryRepository extends CrudRepository<StudentHistory, Long>, FindFirst<StudentHistory> {}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM