4

In my current project I'm using Spring Data JPA and I have more than 20 @Entity classes.

I want to create repositories for them, but creating another classes, each for any model, with @Repository annotation seems to be some kind of overkill and a lot of "repeated" code - all repository classes will look like:

@Repository
public interface SomeModelRepository extends CrudRepository<SomeModel, Long> {
}

There is any way to create "automagically" those repositories? And specify only those that I will extend with my own methods? How should be that done in DRY and KISS way?

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
elwin013
  • 519
  • 10
  • 19
  • 2
    There isn't... You need the interface. Also you don't need `@Repository` it adds nothing. – M. Deinum Feb 04 '16 at 12:56
  • 1
    once your project grows and you have the need to add additional queries for one or more of your entities, you'll be glad to have a separate interface defined for each of them – marco.eig Feb 04 '16 at 13:11
  • 3
    In addition to that - do you really need a repository for each entity? Usually some entities are the owners / [aggregate roots](http://stackoverflow.com/q/1958621/466738) and the rest are only their children and will not be queried directly. – Adam Michalik Feb 04 '16 at 15:37
  • Ok, thank you for replies. I will reconsider if there is need for a repository for each entity and create only truly needed. – elwin013 Feb 05 '16 at 11:59

3 Answers3

3

As mentioned in comments above (in question) - no, I have to create repository for every entity I need. Also it is worth to think about aggregate roots (if some entities won't be queried directly).

Community
  • 1
  • 1
elwin013
  • 519
  • 10
  • 19
2

When I worked with a datasource with about 100+ entities I used the following method so that do not create repositories for each entity. I worked mainly for only saving information from the source to our database. However have some idea how to retrieve data also.

The main idea to create @MappedSuperclass:

@MappedSuperclass
public abstract class SuperClass {

    @Id
    @GeneratedValue
    private Integer id;

    public SuperClass() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

Then extends all other entities:

@Entity
public class Class1 extends SuperClass {

    private String classInfo;

    public String getClassInfo() {
        return classInfo;
    }

    public void setClassInfo(String classInfo) {
        this.classInfo = classInfo;
    }

    @Override
    public String toString() {
        return "\nClass1{" +
                "classInfo='" + classInfo + '\'' +
                '}';
    }
}

@Entity
public class Class2 extends SuperClass {

    private String classInfo;

    public String getClassInfo() {
        return classInfo;
    }

    public void setClassInfo(String classInfo) {
        this.classInfo = classInfo;
    }

    @Override
    public String toString() {
        return "\nClass2{" +
                "classInfo='" + classInfo + '\'' +
                '}';
    }
}

Your repository in this case will be:

public interface SuperRepository extends JpaRepository<SuperClass, Integer> {

}

And you can apply it:

    Class1 class1 = new Class1();
    class1.setClassInfo("Class1 info");

    Class2 class2 = new Class2();
    class2.setClassInfo("Class2 info");

    superRepository.save(class1);
    superRepository.save(class2);

    //OR 
    //List<SuperClass> entities = Arrays.asList(class1,class2);
    //superRepository.saveAll(entities);

Hibernate will create Class1 and Class2 tables and fill them with data.

The next step - how to retrieve the data. I would think about such method - create queries for each class in this one repository:

public interface SuperRepository extends JpaRepository<SuperClass, Integer> {

    @Query("select c from Class1 c")
    List<Class1> findAllClass1();

    @Query("select c from Class2 c")
    List<Class2> findAllClass2();

}

Then when you apply this:

    System.out.println(superRepository.findAllClass1());
    System.out.println(superRepository.findAllClass2());

you will get:

[Class1{classInfo='Class1 info'}]

[Class2{classInfo='Class2 info'}]

Community
  • 1
  • 1
Kirill Ch
  • 5,496
  • 4
  • 44
  • 65
  • 1
    I am not upvoting because it doen't answer the question as asked. But I really like the approach you suggest. Am gonna use that. – Shashikant Soni Aug 22 '19 at 08:25
0

For people who land here looking for ways to avoid too many repository interface files. There is a thing called 'Composable Repositories' in Spring as of 5.0 and there are quite a few sample codes available so am not going to re-explain that here. I found a similar way to avoid too many files as below: Create a normal class which becomes the supplier of all those empty repository interfaces. Define the @Repository interfaces as non-public interfaces within that containing class and write getters for each of those repositories. Sample below:

Public MyRepositoryProvider {

  @Autowired 
  Class1Repository class1Repo;
  public Class1Repository getClass1Repo() {
      return class1Repo;

  //.... similarly for Class2Repo

}

@Repository
interface Class1Repository extends CRUDRepository<Class1, Long>{}
@Repository
interface Class2Repository extends CRUDRepository<Class2, Long>{}

Put all this is in a single java file. And make sure its in path of repository scans for your project.

  • I don't think that actually works, because the interfaces are not defined in an extra file and therefore are not public. You could create only a super class in an extra file though, let's say `NestedRepository` which all nested repositories extend (instead of CrudRepository). The members of the `RepositoryProvider` than have to be of type `NestedRepository`. Because the super interface is defined in it's own file, is has a public visibility. – Melkor Dec 20 '19 at 15:18