如何通过 Xml 配置连接 (subclass/subinterface of a) JpaRepository?
How do I wire a (subclass/subinterface of a) JpaRepository via Xml configuration?
如何通过 Xml 配置连接 (subclass/subinterface) JpaRepository?
所以我有一个 "implementation" 的 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;
}
}
和一个 class 我需要在其中注入 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;
}
}
和 "manager" 的接口以确保完整性。
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>
因此,当您尝试 xml 定义 IoC/DI.. 您会得到“非抽象 bean 不允许接口 ”。
这看起来像个第 22 条军规……:(
神奇的问题:
如何使用 xml-config for 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 -->
<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}
基于参考文档:XML Configuration
以下配置基于对
的理解
Each bean is registered under a bean name that is derived from the
interface name, so an interface of UserRepository would be registered
under 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;
Spring 引导版本:2.2.6
此外 documentation 提到了以下内容
One way to do so is by using the Spring namespace that is shipped with
each Spring Data module that supports the repository mechanism,
although we generally recommend using Java configuration.
注意:修改了包名以匹配问题,请按要求修改。
希望对您有所帮助。
另一个答案比这个答案更简单也更好。 (来自 "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"/>
以上是使用
的丑陋和讨厌的解决方法
"Instantiation by Using an Instance Factory Method"
如何通过 Xml 配置连接 (subclass/subinterface) JpaRepository?
所以我有一个 "implementation" 的 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;
}
}
和一个 class 我需要在其中注入 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;
}
}
和 "manager" 的接口以确保完整性。
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>
因此,当您尝试 xml 定义 IoC/DI.. 您会得到“非抽象 bean 不允许接口 ”。
这看起来像个第 22 条军规……:(
神奇的问题:
如何使用 xml-config for 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 -->
<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}
基于参考文档:XML Configuration
以下配置基于对
的理解Each bean is registered under a bean name that is derived from the interface name, so an interface of UserRepository would be registered under 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;
Spring 引导版本:2.2.6
此外 documentation 提到了以下内容
One way to do so is by using the Spring namespace that is shipped with each Spring Data module that supports the repository mechanism, although we generally recommend using Java configuration.
注意:修改了包名以匹配问题,请按要求修改。
希望对您有所帮助。
另一个答案比这个答案更简单也更好。 (来自 "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"/>
以上是使用
的丑陋和讨厌的解决方法"Instantiation by Using an Instance Factory Method"