Search code examples
javaspringhibernateinheritancerepository

One method to save entity which extand abstract class in repository


I am new at abstract class as Entity so for training I created abstract class

@Entity
@Inheritance(strategy = TABLE_PER_CLASS)
@Getter
@Setter
@NoArgsConstructor
public abstract class ShapeEntity {
     @Id
     @GeneratedValue(generator = "system-uuid")
     @GenericGenerator(name = "system-uuid", strategy = "uuid")
     private String id = UUID.randomUUID().toString();
}

and classes which extand Shape:

public class SquareEntity extends ShapeEntity {
    ...
}
public class CircleEntity extends ShapeEntity {
    ...
}

every entity has repostiory like that:

public interface CircleEntityRepository extends JpaRepository<CircleEntity, String> {
}

When I want to update entity I am searching it in every repository, next (if entity was found) map parameters and finnaly have to save that entity in database. How can I easy decide which repository should be used for it? I created method save with if else which now works:

public ShapeEntity save(ShapeEntity shapeEntity) {
    if (shapeEntity.getClass().getName().contains("CircleEntity")) {
        return circleEntityRepository.save((CircleEntity) shapeEntity);
    } else if (shapeEntity.getClass().getName().contains("SquareEntity")) {
        return squareEntityRepository.save((SquareEntity) shapeEntity);
    } else {
        throw new EntityNotFoundException();
    }
}

but it is a bit uncomfortable becouse when I will have 20 entities I will have to create 20 else if loops. Can I do any interface for this or something like that?


Solution

  • If you create a ShapeEntityRepository for the ShapeEntity class you don't need if-else blocks.

    public interface ShapeEntityRepository
            extends JpaRepository<ShapeEntity, String>, 
            JpaSpecificationExecutor<ShapeEntity> {}
    

    ShapeEntityRepository is useable for all classes that are extended from the ShapeEntity class.

    ShapeEntity circleEntity = new CircleEntity();
    ShapeEntity squareEntity = new SquareEntity();
    
    shapeEntityRepository.save(circleEntity);
    shapeEntityRepository.save(squareEntity);
    

    Alternatively, if there must be different repositories, you can use Spring's GenericTypeResolver to get the repository of the class.

    @Component
    public class RepositoryHolder {
    
        private final List<JpaRepository> jpaRepositories;
    
        public RepositoryHolder(List<JpaRepository> jpaRepositories) {
            this.jpaRepositories = jpaRepositories;
        }
    
        public JpaRepository getRepositoryBy(Class<?> domainClass) {
            Optional<JpaRepository> optionalRepository = jpaRepositories.stream()
                    .filter(repository -> GenericTypeResolver
                            .resolveTypeArguments(
                                    repository.getClass(),
                                    Repository.class
                            )[0]
                            .equals(domainClass))
                    .findFirst();
            if (optionalRepository.isPresent()) {
                return optionalRepository.get();
            } else throw new IllegalArgumentException();
        }
    }
    

    If you want to use it, your save(...) method will be something like this:

    public void save(ShapeEntity shapeEntity) {
        JpaRepository repository = repositoryHolder
                .getRepositoryBy(shapeEntity.getClass());
        repository.save(shapeEntity);
    }
    

    Note: JPA Repository allows to use default CRUD functions, you can add an intermediate repository bean like here and reduce these codes to that intermediate repository.