java Spring/Hibernate/MySQL/JPA实体未保存
我正在尝试配置一个应用程序来使用spring数据jpa 1.11.13 hibernate 5.2.10 spring 4.3.11。释放
问题是实体没有被持久化到MySQL数据存储中
POM相关性
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.10.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.13.RELEASE</version>
</dependency>
</dependencies>
MySQL DDL脚本
DROP TABLE IF EXISTS `property` ;
CREATE TABLE IF NOT EXISTS `property` (
`id` VARCHAR(60) NOT NULL,
`supplier_id` VARCHAR(30) NOT NULL,
`url` VARCHAR(255) NOT NULL,
`main_pic` VARCHAR(255) NOT NULL,
`lat` DOUBLE NOT NULL,
`lng` DOUBLE NOT NULL,
`sys_type` VARCHAR(30) NOT NULL,
`name` VARCHAR(255) NOT NULL,
`description` TEXT NOT NULL,
`one_liner` VARCHAR(255) NOT NULL DEFAULT '',
`num_bedrooms` TINYINT(127) NOT NULL,
`num_bathrooms` TINYINT(127) NOT NULL,
`max_occ` TINYINT(127) NOT NULL,
`country` CHAR(2) NOT NULL,
`rating` DOUBLE NOT NULL DEFAULT 0,
`num_raters` INT NOT NULL DEFAULT 0,
`rank` DOUBLE NOT NULL DEFAULT 0,
`lowest_ppn` DOUBLE NOT NULL DEFAULT 0,
`max_ppn` DOUBLE NOT NULL DEFAULT 0,
`avg_ppn` DOUBLE NOT NULL DEFAULT 0,
`private_pool` TINYINT(1) NOT NULL DEFAULT 0,
`pool` TINYINT(1) NOT NULL DEFAULT 0,
`internet_access` TINYINT(1) NOT NULL DEFAULT 0,
`air_conditioning` TINYINT(1) NOT NULL DEFAULT 0,
`bbq` TINYINT(1) NOT NULL DEFAULT 0,
`satellite` TINYINT(1) NOT NULL DEFAULT 0,
`hot_tub` TINYINT(1) NOT NULL DEFAULT 0,
`sauna` TINYINT(1) NOT NULL DEFAULT 0,
`parking` TINYINT(1) NOT NULL DEFAULT 0,
`instant_book` TINYINT(1) NOT NULL DEFAULT 0,
`updated` DATETIME NOT NULL DEFAULT NOW(),
PRIMARY KEY (`id`, `supplier_id`))
ENGINE = InnoDB;
实体(用于保存字符的equals/hashcode/Getter/Setter)
@Entity
@Table(name="property")
public class Property {
@EmbeddedId
private PropertyCompositeKey id;
@NotNull
@Column(name="url")
private String url;
@Column(name="main_pic")
@NotNull
private String mainPic;
@Column(name="lat")
@Min(-180)
@Max(180)
@NotNull
private Double lat;
@Column(name="lng")
@Min(-90)
@Max(90)
@NotNull
private Double lng;
@Column(name="type")
@NotNull
private String externalType;
@Column(name="name")
@NotNull
private String name;
@Column(name="description")
@NotNull
private String description;
@Column(name="num_bedrooms")
@Min(1)
@NotNull
private Integer numBedrooms;
@Column(name="num_bathrooms")
@Min(1)
@NotNull
private Integer numBathrooms;
@Column(name="max_occ")
@Min(1)
@NotNull
private Integer maxOcc;
@Column(name="country")
@Length(min=2,max=2)
@NotNull
private String country;
@Column(name="rating")
private Double rating;
@Column(name="num_raters")
private Integer numRaters;
@Column(name="rank")
private Double rank;
@Column(name="lowest_ppn")
@Min(1)
private Double lowestPpn;
@Column(name="max_ppn")
@Min(1)
private Double maxPpn;
@Column(name="avg_ppn")
@Min(1)
private Double avgPpn;
@Column(name="private_pool")
private Boolean privatePool;
@Column(name="pool")
private Boolean pool;
@Column(name="internet_access")
private Boolean internetAccess;
@Column(name="air_conditioning")
private Boolean airConditioning;
@Column(name="bbq")
private Boolean bbq;
@Column(name="satellite")
private Boolean satellite;
@Column(name="hot_tub")
private Boolean hotTub;
@Column(name="sauna")
private Boolean sauna;
@Column(name="parking")
private Boolean parking;
@Column(name="instant_book")
private Boolean instantBook;
@Column(name="updated")
@Version
private Timestamp updated;
public void setKey(String id, String supplierId) {
PropertyCompositeKey pck = new PropertyCompositeKey();
pck.setId(id);
pck.setSupplierId(supplierId);
this.id = pck;
}
}
可嵌入-复合主键
@Embeddable
public class PropertyCompositeKey implements Serializable {
/**
*
*/
private static final long serialVersionUID = 6575008230123343148L;
@Column(name="id")
@NotNull
private String id;
@Column(name="supplier_id")
@NotNull
private String supplierId;
}
存储库
@Repository
public interface PropertyRepository extends JpaRepository<Property, PropertyCompositeKey> {
}
服务接口
public interface PropertiesService {
Property save(Property property);
}
服务实现
@Service
public class PropertyServiceImpl implements PropertiesService {
@Autowired
private PropertyRepository repo;
@Override
@Transactional
public Property save(Property property) {
Property saved = repo.save(property);
return saved;
}
}
Spring配置类
@Configuration
@EnableJpaRepositories("property.dao")
@EnableTransactionManagement
@PropertySource(
value={"classpath:/properties.properties"},
ignoreResourceNotFound = false)
@ComponentScan(basePackages={"property.properties"})
public class PropertySpringConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public DataSource dataSource(@Value("${jdbc.driverClassName}") String driverClass,
@Value("${jdbc.url}") String url,
@Value("${jdbc.username}") String un,
@Value("${jdbc.password}") String pw,
@Value("${jdbc.minIdleConnections}") int mi,
@Value("${jdbc.initialPoolSize}") int is) {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(driverClass);
ds.setUrl(url);
ds.setUsername(un);
ds.setPassword(pw);
ds.setMinIdle(mi);
ds.setInitialSize(is);
return ds;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(new String[] { "property.entity" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
@Bean
public PlatformTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactory, DataSource ds) {
JpaTransactionManager jtm = new JpaTransactionManager();
jtm.setEntityManagerFactory(entityManagerFactory.getNativeEntityManagerFactory());
jtm.setDataSource(ds);
return jtm;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty(
"hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
}
测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class,classes=PropertySpringConfig.class)
public class TestRepository {
@Autowired
private PropertiesService propertyRepository;
@Test
@Commit
public void testRepository() {
Property p = new Property();
p.setKey("TEST_ID", "TEST_SUPPLIER");
p.setAirConditioning(true);
p.setAvgPpn(500.0);
p.setCountry("ES");
p.setDescription("TEST DESCRIPTION");
p.setExternalType("TEST");
p.setHotTub(false);
p.setLat(12.5);
p.setLng(45.6);
p.setLowestPpn(150.4);
p.setMainPic("TEST");
p.setMaxOcc(5);
p.setMaxPpn(777.4);
p.setName("TEST NAME");
p.setNumBathrooms(3);
p.setNumBedrooms(7);
p.setNumRaters(5);
p.setPrivatePool(true);
p.setRank(1.0);
p.setRating(4.5);
p.setUrl("TEST");
Property s = propertyRepository.save(p);
}
}
当我运行测试时,它会毫无错误地完成,但实体不会持久化。注销以保存:
DEBUG: org.springframework.orm.jpa.JpaTransactionManager - Creating new transaction with name [property.service.PropertyServiceImpl.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG: org.springframework.orm.jpa.JpaTransactionManager - Opened new EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG: org.hibernate.engine.transaction.internal.TransactionImpl - begin
DEBUG: org.springframework.orm.jpa.JpaTransactionManager - Not exposing JPA transaction [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] as JDBC transaction because JpaDialect [org.springframework.orm.jpa.DefaultJpaDialect@ccd1bc3] does not support JDBC Connection retrieval
DEBUG: org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor$CustomAnnotationTransactionAttributeSource - Adding transactional method 'save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG: org.springframework.orm.jpa.JpaTransactionManager - Found thread-bound EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG: org.springframework.orm.jpa.JpaTransactionManager - Participating in existing transaction
DEBUG: org.springframework.orm.jpa.EntityManagerFactoryUtils - Opening JPA EntityManager
DEBUG: org.springframework.orm.jpa.EntityManagerFactoryUtils - Registering transaction synchronization for JPA EntityManager
DEBUG: org.hibernate.event.internal.AbstractSaveEventListener - Generated identifier: component[id,supplierId]{supplierId=TEST_SUPPLIER, id=TEST_ID}, using strategy: org.hibernate.id.CompositeNestedGeneratedValueGenerator
DEBUG: org.springframework.orm.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
DEBUG: org.springframework.orm.jpa.JpaTransactionManager - Initiating transaction commit
DEBUG: org.springframework.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
DEBUG: org.hibernate.engine.transaction.internal.TransactionImpl - committing
DEBUG: org.springframework.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction
DEBUG: org.springframework.orm.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
DEBUG: org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext@50f8360d testClass = TestRepository, testInstance = property.TestRepository@61a485d2, testMethod = testRepository@TestRepository, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2cb4c3ab testClass = TestRepository, locations = '{}', classes = '{class property.spring.config.PropertySpringConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.AnnotationConfigContextLoader', parent = [null]]], class annotated with @DirtiesContext [false] with mode [null], method annotated with @DirtiesContext [false] with mode [null].
DEBUG: org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test class: context [DefaultTestContext@50f8360d testClass = TestRepository, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2cb4c3ab testClass = TestRepository, locations = '{}', classes = '{class property.spring.config.PropertySpringConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.AnnotationConfigContextLoader', parent = [null]]], class annotated with @DirtiesContext [false] with mode [null]. INFO : org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@643b1d11: startup date [Sat Jun 23 12:53:04 BST 2018]; root of context hierarchy
INFO : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
DEBUG: org.hibernate.internal.SessionFactoryImpl - HHH000031: Closing
DEBUG: org.hibernate.engine.spi.CascadeStyles - External cascade style regsitration [persist : STYLE_PERSIST] overrode base registration [STYLE_PERSIST_SKIPLAZY]
DEBUG: org.hibernate.service.internal.AbstractServiceRegistryImpl - Implicitly destroying ServiceRegistry on de-registration of all child ServiceRegistries
DEBUG: org.hibernate.boot.registry.internal.BootstrapServiceRegistryImpl - Implicitly destroying Boot-strap registry on de-registration of all child ServiceRegistries
当我将PropertyServiceImpl
中的方法更改为使用JpaRepository
中的saveAndFlush
方法时,测试包含以下带有错误的日志输出:
DEBUG: org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl
- JDBC transaction marked for rollback-only (exception provided for stack trace) java.lang.Exception: exception just for purpose of providing stack trace at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.markRollbackOnly(JdbcResourceLocalTransactionCoordinatorImpl.java:255) at org.hibernate.engine.transaction.internal.TransactionImpl.setRollbackOnly(TransactionImpl.java:143) at org.springframework.orm.jpa.JpaTransactionManager$JpaTransactionObject.setRollbackOnly(JpaTransactionManager.java:655) at org.springframework.orm.jpa.JpaTransactionManager.doSetRollbackOnly(JpaTransactionManager.java:566) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:860) at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:830) at org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionAspectSupport.java:522) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:286) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy51.saveAndFlush(Unknown Source) at property.service.PropertyServiceImpl.save(PropertyServiceImpl.java:19) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy52.save(Unknown Source) at TestRepository.testRepository(TestRepository.java:62) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
我看不到我错过了什么
你能告诉我我能做些什么来修复这个问题,并将实体持久化到数据存储中吗
# 1 楼答案
您的问题是由于配置错误,HibernateJavaEndorapter不在Spring上下文中,因此您必须像Spring Bean一样声明它,示例如下:
# 2 楼答案
问题是在初始化
PlatformTransactionManager
bean时使用LocalContainerEntityManagerFactoryBean
,然后使用getNativeEntityManagerFactory()
方法。初始化事务管理器的正确方法是:让Spring决定要通过的
EntityManagerFactory
观察了本文中的正确配置: https://docs.spring.io/spring-data/jpa/docs/1.11.13.RELEASE/reference/html/#repositories.create-instances.java-config