【问题标题】:How do I wire a (subclass/subinterface of a) JpaRepository via Xml configuration?如何通过 Xml 配置连接(a)JpaRepository 的(子类/子接口)?
【发布时间】:2020-08-31 10:41:50
【问题描述】:

如何通过 Xml 配置连接 JpaRepository 的(子类/子接口)?

所以我有一个 JpaRepository 的“实现”

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

import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.Optional;

public interface MyDepartmentJpaRepo extends JpaRepository<Department, Long> {

   /* "lookup strategy".  see https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods */
    Optional<Department> findDepartmentByDepartmentNameEquals(String departmentName);

    Collection<Department> findByCreateOffsetDateTimeBefore(OffsetDateTime zdt);

}

和实体

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.OffsetDateTime;

@Entity
@Table(name = "DepartmentTable")
public class Department {

    @Id
    @Column(name = "DepartmentKey", unique = true)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long departmentKey;

    @Column(name = "DepartmentName", unique = true)
    private String departmentName;

    @Column(name = "CreateOffsetDateTime", columnDefinition = "TIMESTAMP WITH TIME ZONE" )
    private OffsetDateTime createOffsetDateTime;

    public long getDepartmentKey() {
        return departmentKey;
    }

    public void setDepartmentKey(long departmentKey) {
        this.departmentKey = departmentKey;
    }

    public String getDepartmentName() {
        return departmentName;
    }

    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }

    public OffsetDateTime getCreateOffsetDateTime() {
        return createOffsetDateTime;
    }

    public void setCreateOffsetDateTime(OffsetDateTime createOffsetDateTime) {
        this.createOffsetDateTime = createOffsetDateTime;
    }
}

还有一个我需要注入 MyDepartmentJpaRepo 的类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

public class DepartmentManager implements IDepartmentManager {

    private final Logger logger;
    private final MyDepartmentJpaRepo deptRepo;

    /* The Inject annotation marks which constructor to use for IoC when there are multiple constructors */
    @Inject
    public DepartmentManager(MyDepartmentJpaRepo deptRepo) {
        this(LoggerFactory.getLogger(DepartmentManager.class), deptRepo);
    }

    public DepartmentManager(Logger lgr, MyDepartmentJpaRepo deptRepo) {
        if (null == lgr) {
            throw new IllegalArgumentException("Logger is null");
        }

        if (null == deptRepo) {
            throw new IllegalArgumentException("IDepartmentDomainData is null");
        }

        this.logger = lgr;
        this.deptRepo = deptRepo;
    }

    @Override
    public Collection<Department> getAll() {
        List<Department> returnItems = this.deptRepo.findAll();
        return returnItems;
    }

    @Override
    public Optional<Department> getSingle(long key) {
        Optional<Department> returnItem = this.deptRepo.findById(key);
        return  returnItem;
    }

    @Override
    public Optional<Department> getSingleByName(String deptName) {
        Optional<Department> returnItem = this.deptRepo.findDepartmentByDepartmentNameEquals(deptName);
        return  returnItem;
    }

    public Collection<Department> getDepartmentsOlderThanDate(OffsetDateTime zdt)
    {
        Collection<Department> returnItems = this.deptRepo.findByCreateOffsetDateTimeBefore(zdt);
        return returnItems;
    }

    @Override
    public Department save(Department item) {
        Department returnItem = this.deptRepo.save(item);
        return  returnItem;
    }
}

以及“经理”的界面以确保完整性。

import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.Optional;

public interface IDepartmentManager {

    Collection<Department> getAll();

    Optional<Department> getSingle(long key);

    Optional<Department> getSingleByName(String deptName);

    Department save(Department item);

    Collection<Department> getDepartmentsOlderThanDate(OffsetDateTime zdt);
}

问题出在 applicationcontext.xml 中。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="jpaSetup.di.xml"/>

    <bean id="MyDepartmentJpaRepoBean" class="com.mycompany.blah.blah.blah.MyDepartmentJpaRepo">
    </bean>

    <bean id="IDepartmentManagerBean" class="com.mycompany.blah.blah.blah.DepartmentManager">
        <constructor-arg ref="MyDepartmentJpaRepoBean"/>
    </bean>



</beans>

所以..spring-boot-data 将(子接口的 JpaRepository)定义为一个接口

interface MyDepartmentJpaRepo extends JpaRepository<Department, Long>

因此,当您尝试对 IoC/DI 进行 xml 定义时,您会得到“interface not allowed for non-abtract beans”。

这看起来像是第 22 条 ...... :(

神奇的问题:

如何将 xml-config 用于 IoC/DI ......并利用子接口 JpaRepository ????

追加:

如果我添加“jpa:repositories”,那么我没有“manager”的构造函数参数。

http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

<import resource="jpaSetup.di.xml"/>

<jpa:repositories base-package="com.mycompany.blah.blah.blah" />

--> -->

<bean id="IDepartmentManagerBean" class="com.mycompany.organizationdemo.businesslayer.managers.DepartmentManager">
    <constructor-arg ref="NotDoesNotExistMyDepartmentJpaRepoBean"/> <!-- DOES NOT WORK -->
</bean>

........

下面的其他文件以确保完整性。

jpaSetup.di.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <beans>

        <bean id="myLocalContainerEntityManagerFactoryBeanBean"
              class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="packagesToScan" value="com.blah.blah.blah.entities"/>
            <property name="jpaVendorAdapter">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                    <property name="showSql" value="${spring.jpa.show-sql}"/>
                    <property name="generateDdl" value="${spring.jpa.generate-ddl}"/>
                </bean>
            </property>
            <!-- See https://stackoverflow.com/questions/16088112/how-to-auto-detect-entities-in-jpa-2-0/16088340#16088340 -->
            <property name="jpaProperties">
                <props>
                    <prop key="hibernate.hbm2ddl.auto">${spring.jpa.hibernate.ddl-auto}</prop>
                    <prop key="hibernate.dialect">${spring.jpa.properties.hibernate.dialect}</prop>
                </props>
            </property>
        </bean>

        <bean id="dataSource"
              class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="url" value="${SPRING_DATASOURCE_URL}"/>
            <property name="username" value="${SPRING_DATASOURCE_USERNAME}"/>
            <property name="password" value="${SPRING_DATASOURCE_PASSWORD}"/>
            <property name="driverClassName" value="${SPRING_DATASOURCE_DRIVER-CLASS-NAME}"/>
        </bean>


        <bean id="transactionManager"
              class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="myLocalContainerEntityManagerFactoryBeanBean"/>
        </bean>

        <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

        <bean id="persistenceExceptionTranslationPostProcessor" class=
                "org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

    </beans>

</beans>

和application.yml

spring:
  jpa:
    generate-ddl: true
    show-sql: true
    hibernate:
      ddl-auto: update
      naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
    properties:
      hibernate:
        dialect: org.hibernate.dialect.H2Dialect
  datasource:
    url: ${SPRING_DATASOURCE_URL}
    username: ${SPRING_DATASOURCE_USERNAME}
    password: ${SPRING_DATASOURCE_PASSWORD}
    driverClassName: ${SPRING_DATASOURCE_DRIVER-CLASS-NAME}

【问题讨论】:

    标签: spring-boot java-8 spring-data-jpa java-11


    【解决方案1】:

    基于参考文档:XML Configuration

    以下配置工作基于以下理解:

    每个 bean 都在一个 bean 名称下注册,该名称源自 接口名称,因此将注册 UserRepository 的接口 在 userRepository 下。

    接口的bean名称

    public interface MyDepartmentJpaRepo extends JpaRepository<Department, Long> {
    ..
    }
    

    如下:myDepartmentJpaRepo

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/data/jpa
         https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    
       <jpa:repositories base-package="com.mycompany.organizationdemo.businesslayer.repository"/>
    
        <bean id="IDepartmentManagerBean" class="com.mycompany.organizationdemo.businesslayer.managers.DepartmentManager">
            <constructor-arg ref="myDepartmentJpaRepo"/>
        </bean>
    </beans>
    

    为了验证,我自动装配并使用了 DepartmentManager 实例,如下所示

    @Autowired
    IDepartmentManager manager;
    

    春季启动版本:2.2.6

    documentation 也提到了以下内容

    一种方法是使用随附的 Spring 命名空间 每个支持存储库机制的 Spring Data 模块, 虽然我们通常建议使用 Java 配置。

    注意:包名已修改为匹配问题,请按要求修改。

    希望这会有所帮助。

    【讨论】:

    • 哇。就这么简单。第一个字母小写。我以前确实知道那个spring-di voodoo。谢谢。
    • 感谢文档参考。而不仅仅是简单的答案。 :)
    【解决方案2】:

    另一个答案比这个答案更简单更好。 (来自“R.G.”的答案)。但是,我确实为未来的读者找到了一个丑陋的解决方法。

    不要选择这个答案而不是 R.G. 的答案。 !

    import org.springframework.stereotype.Component;
    
    import javax.inject.Inject;
    
    @Component
    public class DepartmentJpaRepositoryFactory {
    
        @Inject
        private MyDepartmentJpaRepo autoinjectedDepartmentJpaRepositoryWorkaround;
    
        /* this is a workaround for using explicit xml IOC with spring-data.
        * because it is "interface MyDepartmentJpaRepo" (not a class), you cannot do traditional xml IoC definitions :(
        * this is a workaround.  this factory should NEVER be used by the code base, only by spring-di  */
        public MyDepartmentJpaRepo getInstanceDepartmentJpaRepository() {
            return this.autoinjectedDepartmentJpaRepositoryWorkaround;
        }
    }
    

    然后是 applicationcontext.xml

        <jpa:repositories base-package="com.blah.blah.blah.jpa.repositories" />
    
    <bean id="DepartmentManagerBean" class="com.blah.blah.blah.managers.DepartmentManager">
        <constructor-arg ref="DepartmentJpaRepositoryViaFactoryMethodBean"/>
    </bean>
    
    <bean id="DepartmentJpaRepositoryServiceLocatorBean" class="com.blah.blah.blah.jpa.factories.DepartmentJpaRepositoryFactory">
        <!-- inject any dependencies required by this locator bean -->
    </bean>
    
    <bean id="DepartmentJpaRepositoryViaFactoryMethodBean"
          factory-bean="DepartmentJpaRepositoryServiceLocatorBean"
          factory-method="getInstanceDepartmentJpaRepository"/>
    

    以上是使用

    的丑陋和讨厌的解决方法

    https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-class-instance-factory-method

    “使用实例工厂方法实例化”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-23
      • 1970-01-01
      • 2012-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多