0

I have the below code.

Note that I have an interface MySuperCoolEntityRepositoryContract.

And I have a "concrete interface" MySuperCoolEntityJpaRepository that implements my above MySuperCoolEntityRepositoryContract interface and JpaRepository.

All of that works fine with @ComponentScan.

I am changing my code to "java config", aka a centralized location where I can code up my DI definitions. (Also known as CompositionRoot in some circles).

The issue is when I try to "code up" the concrete for the interface. (Skip down to later in this question.

package com.me.domain.jpaentities;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;

@Entity(name = "MySuperCoolEntityTableName")
public class MySuperCoolEntity implements Serializable {

    @Id
    @Column(name = "CoolSurrogateKeyColumn")
    private String coolSurrogateKey;

    @Column(name = "CoolMagicValueColumn")
    private String coolMagicValue;


    public String getCoolSurrogateKey() {
        return this.coolSurrogateKey;
    }

    public void setCoolSurrogateKey(String coolSurrogateKey) {
        this.coolSurrogateKey = coolSurrogateKey;
    }

    public String getCoolMagicValue() {
        return this.coolMagicValue;
    }

    public void setCoolMagicValue(String coolMagicValue) {
        this.coolMagicValue = coolMagicValue;
    }

}

===============

package com.me.dal.repositories.interfaces;

import com.me.domain.jpaentities.MySuperCoolEntity;
import java.util.Collection;

public interface MySuperCoolEntityRepositoryContract {

    Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);

}

=========================

package com.me.dal.repositories;

import com.me.dal.repositories.interfaces.MySuperCoolEntityRepositoryContract;
import com.me.domain.jpaentities.MySuperCoolEntity;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Collection;

@Repository
public interface MySuperCoolEntityJpaRepository extends MySuperCoolEntityRepositoryContract, JpaRepository<MySuperCoolEntity,String> {

    Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);

}

Now this issue.

package com.me.myapplication.configuration;

import com.me.dal.repositories.MySuperCoolEntityJpaRepository;
import com.me.dal.repositories.interfaces.MySuperCoolEntityRepositoryContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyCompositionRoot {


    @Bean
    public MySuperCoolEntityRepositoryContract getAMySuperCoolEntityRepositoryContract()
    {
        return new MySuperCoolEntityJpaRepository();  /* << issue is here, this is an abstract class, aka, an interface with some methods defined */
    }

}

Using the super cool JpaRepository "concrete interface" aka "really an abstract class but called an interface" aka "Interface Default Methods" ( see https://dzone.com/articles/interface-default-methods-java ) ........

The exact error is:

MySuperCoolEntityJpaRepository is abstract; cannot be instantiated

I do understand the error. MySuperCoolEntityJpaRepository is abstract. I get that.

But with this super cool "just extend JpaRepository and get all kinds of default functionality".....

How do I register a concrete JpaRepository with Spring DI (specifically with "code it up" java config ?

............

I tried making it a "class".

public class MySuperCoolEntityJpaRepository extends MySuperCoolEntityRepositoryContract, JpaRepository<MySuperCoolEntity,String>

but that wants me to define all those built in methods like "findAll",etc, etc.

granadaCoder
  • 26,328
  • 10
  • 113
  • 146

2 Answers2

0

Spring boot magically provides implementation for the methods defined in your interface. The @EnableJpaRepositories scans all packages below the package for interfaces extending JpaRepository and creates a Spring bean for it that is backed by an implementation of SimpleJpaRepository (spring data provides default imlpementations of CRUD repository through this class).

Your interface MySuperCoolEntityJpaRepository extends the interface MySuperCoolEntityRepositoryContract , but you only extend the JpaRepository on the interface MySuperCoolEntityJpaRepository which means spring will only provide the default implementations for methods in the interface MySuperCoolEntityJpaRepository . So try it like :

public interface MySuperCoolEntityRepositoryContract extends JpaRepository<MySuperCoolEntity,String>{

    Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);

}

then extend this in your repository like :

@Repository
public interface MySuperCoolEntityJpaRepository extends MySuperCoolEntityRepositoryContract {

    Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);

}

Related Post : how annotation @Repository in java spring work?

Ananthapadmanabhan
  • 5,706
  • 6
  • 22
  • 39
  • This explains the magic of the default methods (findAll, etc) and adding to the JpaRepository methods). I appreciate the attempt. But the main point of the is about using java-config to code up the DI, not rely on any scanning. Future readers can see this link about some of the "freebies" you get with default methods : https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.core-concepts – granadaCoder Oct 30 '19 at 13:10
0

I figured out a workaround. I don't really like it, but I guess it works.

I also added MySuperCoolEntityBalServiceContract (you can get the idea from just the below), so you know why/how I need to have the getAMySuperCoolEntityRepositoryContract method in my CompositionRoot class below.

I'll leave this (not marked) as the answer in case someone else has a better way, or sees issue(s) with the below. I don't like the EntitiyManager work around, but it got things moving.

package com.me.myapplication.configuration;


import com.me.apicore.managers.MySuperCoolEntityBalService;
import com.me.apicore.managers.interfaces.MySuperCoolEntityBalServiceContract;
import com.me.dal.repositories.interfaces.MySuperCoolEntityRepositoryContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;

import javax.inject.Inject;
import javax.persistence.EntityManager;

@Configuration

public class MyCompositionRoot {

    @Inject
    private EntityManager entManager; /* part of the work around trick */

    @Bean
    public MySuperCoolEntityBalServiceContract getAMySuperCoolEntityBalServiceContract() {
        return new MySuperCoolEntityBalService(this.getAMySuperCoolEntityRepositoryContract());
    }


    @Bean
    public MySuperCoolEntityRepositoryContract getAMySuperCoolEntityRepositoryContract() {
        //return new MySuperCoolEntityJpaRepository(); /* does not work.  :(         */
        RepositoryFactorySupport factory = new JpaRepositoryFactory(entManager);
        MySuperCoolEntityRepositoryContract repository = factory.getRepository(MySuperCoolEntityRepositoryContract.class);
        return repository;
    }
}

And I tweaked this (note the addition of the RepositoryDefinition annotation)

package com.me.dal.repositories.interfaces;

import com.me.domain.jpaentities.MySuperCoolEntity;

import org.springframework.data.repository.RepositoryDefinition;

import java.util.Collection;

@RepositoryDefinition(domainClass = MySuperCoolEntity.class, idClass = String.class)

public interface MySuperCoolEntityRepositoryContract {

    Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);

}
granadaCoder
  • 26,328
  • 10
  • 113
  • 146