如何在没有 Spring 引导的情况下使用 JPA 存储库
How to use JPA Repositories without Spring Boot
我有一个现有的 spring 4 项目(mvc、jdbc 等),我试图将它移植到 spring 引导,但我做不到。 (很多依赖问题,没有人无法解释我该怎么做)。但现在我只想在现有项目中使用 Spring Data JPA。
那就是一个主要的pom依赖:
<properties>
<jetty.version>9.3.5.v20151012</jetty.version>
<spring.version>4.3.12.RELEASE</spring.version>
<spring.boot.version>1.5.8.RELEASE</spring.boot.version>
<spring.security.version>4.2.3.RELEASE</spring.security.version>
<quartz.version>2.2.1</quartz.version>
<slf4j.version>1.7.5</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<hibernate-version>5.3.3.Final</hibernate-version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate-version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate-version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.1.RELEASE</version>
</dependency>
我有现有的配置,我用 entityManagerFactory Bean(在所有示例中使用的)完成。
数据库配置:
@Configuration
@ComponentScan("ru.testproject.*")
@EnableTransactionManagement
@EnableJpaRepositories("ru.testproject.*")
public class DatabaseConfig {
@Value("${postgresql.address}")
String address;
@Value("${postgresql.database}")
String database;
@Value("${postgresql.user}")
String user;
@Value("${postgresql.password}")
String password;
@Value("${db.type}")
String dbType;
@Bean
public DataSource dataSource() {
if (DB_TYPE_POSTGRESQL.equalsIgnoreCase(dbType)) {
return postresqlDataSource();
} else {
return derbyDataSource();
}
}
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
private HikariDataSource derbyDataSource() {
try {
String appHome = System.getProperty(Property.APP_HOME);
Path path = Paths.get(appHome, DATABASE_NAME);
String databaseName = path.toString();
databaseName = StringUtils.replaceChars(databaseName, '\', '/');
Class.forName(DB.DB_DRIVER_DERBY).newInstance();
EmbeddedDataSource embeddedDataSource = new EmbeddedDataSource();
String dbName = databaseName;
embeddedDataSource.setDatabaseName(dbName);
embeddedDataSource.setCreateDatabase("create");
embeddedDataSource.setUser("application");
embeddedDataSource.setPassword("");
HikariConfig config = new HikariConfig();
config.setDataSource(embeddedDataSource);
config.setMaximumPoolSize(10);
config.setAutoCommit(true);
HikariDataSource hikariDataSource = new HikariDataSource(config);
return hikariDataSource;
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new IllegalStateException("error connection Derby");
}
}
private HikariDataSource postresqlDataSource() {
if (StringUtils.isBlank(address)
|| StringUtils.isBlank(database)
|| StringUtils.isBlank(user)
|| StringUtils.isBlank(password)) {
throw new IllegalStateException("error params Postgresql");
}
HikariConfig config = new HikariConfig();
String jdbcUrl = MessageFormat.format(POSTGRESQL, address, database);
config.setJdbcUrl(jdbcUrl);
config.setUsername(user);
config.setPassword(password);
config.setMaximumPoolSize(10);
config.setAutoCommit(false);
return new HikariDataSource(config);
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws SQLException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("ru.testproject.hibernate.*");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
factory.setPersistenceUnitName("test");
return factory;
}
}
用于测试的 Derby 数据库。但主要的数据库类型是带有 HikariCP 的 PostgreSQL。我已经在项目中添加了 HQL 支持并且它工作正常。但是当我尝试添加 JAP 存储库支持时,我遇到了很多问题。我有实体:
@Entity
@Table(name = "users", schema = "public", catalog = "test")
public class Users {
private int id;
private String username;
private String password;
private String email;
@Id
@Column(name = "id", nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Basic
@Column(name = "username", nullable = false, length = 64)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Basic
@Column(name = "password", nullable = false, length = 64)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Basic
@Column(name = "email", nullable = false, length = 128)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Users that = (Users) o;
return id == that.id &&
Objects.equals(username, that.username) &&
Objects.equals(password, that.password) &&
Objects.equals(email, that.email);
}
@Override
public int hashCode() {
return Objects.hash(id, username, password, email);
}
}
和存储库:
@Repository
public interface UsersRepository extends CrudRepository<Users, Long> {
@Query("select b from Users b where b.username = :username")
Users findByName(@Param("username") String username);
}
它只是一个扩展 CrudRepository 的测试存储库。
为了使用该存储库,我创建了带有实现的服务:
@Service
public interface UserService {
void delete(long id);
Users getByName(String name);
Optional<Users> getById(Long id);
Users editUsers(Users user);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UsersRepository usersRepository;
// @PersistenceContext(unitName = "test")
// private EntityManager em;
@Override
public void delete(long id) {
usersRepository.deleteById(id);
}
@Override
public Optional<Users> getById(Long id) {
return usersRepository.findById(id);
}
@Override
public Users getByName(String name) {
return usersRepository.findByName(name);
}
@Override
public Users editUsers(Users user) {
return usersRepository.save(user);
}
}
我正在使用这样的存储库:
@Autowired
private UserService service;
...
Users entry = service.getById(1L).get();
就是这样。
我不使用 Spring 引导(如上所述)并在应用程序失败并出现错误时启动:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in ru.testproject.config.DBConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'entityManagerFactory' threw exception; nested exception is java.lang.NoSuchFieldError: parallelCapableClassLoaderAvailable
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:587)
如果我注释@EnableJpaRepositories注解,我有以下错误:
Unsatisfied dependency expressed through field 'userServiceImpl'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userServiceImpl': Unsatisfied dependency expressed through field 'usersRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ru.testproject.db.hibernate.repository.UsersRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
在第二种情况下,存储库为空,当然 - JPA 存储库已禁用..
请帮忙!我做错了什么,我怎样才能让它发挥作用?我无法通过 google 和互联网课程解决我的问题。可能因为我不明白根本问题是什么。
您似乎没有在组件扫描中添加测试包 ru.testproject.db.hibernate.repository。
感谢大家的帮助!
我已经通过以下更改解决了我的问题:
1) 首先 - 我更新了 spring-context 依赖更新版本:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
它解决了 java.lang.NoClassDefFoundError: org/springframework/context/index/CandidateComponentsIndexLoader 错误。 CandidateComponentsIndexLoader 出现在 spring-context 5.0.0.
之后
2) 我添加了
@EnableJpaRepositories(basePackages = {
"ru.testproject.hibernate"
})
@PropertySource("classpath:testproject.properties")
@ComponentScan("ru.testproject.hibernate")
注释。以前的更改不会影响任何积极的变化。 1) 它导致 entityManagerFactory 错误,我可以用以下方法解决:
3) 使用
为 entityManagerFactory 设置 packgeToScan
entityManagerFactoryBean.setPackagesToScan("ru.testproject.hibernate.entity");
带有实体路径(在这种情况下为用户)。
就是这样。现在存储库工作完美。谢谢大家!!!
ps: 可能是,现在我可以用 spring boot)
我有一个现有的 spring 4 项目(mvc、jdbc 等),我试图将它移植到 spring 引导,但我做不到。 (很多依赖问题,没有人无法解释我该怎么做)。但现在我只想在现有项目中使用 Spring Data JPA。 那就是一个主要的pom依赖:
<properties>
<jetty.version>9.3.5.v20151012</jetty.version>
<spring.version>4.3.12.RELEASE</spring.version>
<spring.boot.version>1.5.8.RELEASE</spring.boot.version>
<spring.security.version>4.2.3.RELEASE</spring.security.version>
<quartz.version>2.2.1</quartz.version>
<slf4j.version>1.7.5</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<hibernate-version>5.3.3.Final</hibernate-version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate-version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate-version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.1.RELEASE</version>
</dependency>
我有现有的配置,我用 entityManagerFactory Bean(在所有示例中使用的)完成。 数据库配置:
@Configuration
@ComponentScan("ru.testproject.*")
@EnableTransactionManagement
@EnableJpaRepositories("ru.testproject.*")
public class DatabaseConfig {
@Value("${postgresql.address}")
String address;
@Value("${postgresql.database}")
String database;
@Value("${postgresql.user}")
String user;
@Value("${postgresql.password}")
String password;
@Value("${db.type}")
String dbType;
@Bean
public DataSource dataSource() {
if (DB_TYPE_POSTGRESQL.equalsIgnoreCase(dbType)) {
return postresqlDataSource();
} else {
return derbyDataSource();
}
}
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
private HikariDataSource derbyDataSource() {
try {
String appHome = System.getProperty(Property.APP_HOME);
Path path = Paths.get(appHome, DATABASE_NAME);
String databaseName = path.toString();
databaseName = StringUtils.replaceChars(databaseName, '\', '/');
Class.forName(DB.DB_DRIVER_DERBY).newInstance();
EmbeddedDataSource embeddedDataSource = new EmbeddedDataSource();
String dbName = databaseName;
embeddedDataSource.setDatabaseName(dbName);
embeddedDataSource.setCreateDatabase("create");
embeddedDataSource.setUser("application");
embeddedDataSource.setPassword("");
HikariConfig config = new HikariConfig();
config.setDataSource(embeddedDataSource);
config.setMaximumPoolSize(10);
config.setAutoCommit(true);
HikariDataSource hikariDataSource = new HikariDataSource(config);
return hikariDataSource;
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new IllegalStateException("error connection Derby");
}
}
private HikariDataSource postresqlDataSource() {
if (StringUtils.isBlank(address)
|| StringUtils.isBlank(database)
|| StringUtils.isBlank(user)
|| StringUtils.isBlank(password)) {
throw new IllegalStateException("error params Postgresql");
}
HikariConfig config = new HikariConfig();
String jdbcUrl = MessageFormat.format(POSTGRESQL, address, database);
config.setJdbcUrl(jdbcUrl);
config.setUsername(user);
config.setPassword(password);
config.setMaximumPoolSize(10);
config.setAutoCommit(false);
return new HikariDataSource(config);
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws SQLException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("ru.testproject.hibernate.*");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
factory.setPersistenceUnitName("test");
return factory;
}
}
用于测试的 Derby 数据库。但主要的数据库类型是带有 HikariCP 的 PostgreSQL。我已经在项目中添加了 HQL 支持并且它工作正常。但是当我尝试添加 JAP 存储库支持时,我遇到了很多问题。我有实体:
@Entity
@Table(name = "users", schema = "public", catalog = "test")
public class Users {
private int id;
private String username;
private String password;
private String email;
@Id
@Column(name = "id", nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Basic
@Column(name = "username", nullable = false, length = 64)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Basic
@Column(name = "password", nullable = false, length = 64)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Basic
@Column(name = "email", nullable = false, length = 128)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Users that = (Users) o;
return id == that.id &&
Objects.equals(username, that.username) &&
Objects.equals(password, that.password) &&
Objects.equals(email, that.email);
}
@Override
public int hashCode() {
return Objects.hash(id, username, password, email);
}
}
和存储库:
@Repository
public interface UsersRepository extends CrudRepository<Users, Long> {
@Query("select b from Users b where b.username = :username")
Users findByName(@Param("username") String username);
}
它只是一个扩展 CrudRepository 的测试存储库。 为了使用该存储库,我创建了带有实现的服务:
@Service
public interface UserService {
void delete(long id);
Users getByName(String name);
Optional<Users> getById(Long id);
Users editUsers(Users user);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UsersRepository usersRepository;
// @PersistenceContext(unitName = "test")
// private EntityManager em;
@Override
public void delete(long id) {
usersRepository.deleteById(id);
}
@Override
public Optional<Users> getById(Long id) {
return usersRepository.findById(id);
}
@Override
public Users getByName(String name) {
return usersRepository.findByName(name);
}
@Override
public Users editUsers(Users user) {
return usersRepository.save(user);
}
}
我正在使用这样的存储库:
@Autowired
private UserService service;
...
Users entry = service.getById(1L).get();
就是这样。 我不使用 Spring 引导(如上所述)并在应用程序失败并出现错误时启动:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in ru.testproject.config.DBConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'entityManagerFactory' threw exception; nested exception is java.lang.NoSuchFieldError: parallelCapableClassLoaderAvailable
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:587)
如果我注释@EnableJpaRepositories注解,我有以下错误:
Unsatisfied dependency expressed through field 'userServiceImpl'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userServiceImpl': Unsatisfied dependency expressed through field 'usersRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ru.testproject.db.hibernate.repository.UsersRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
在第二种情况下,存储库为空,当然 - JPA 存储库已禁用.. 请帮忙!我做错了什么,我怎样才能让它发挥作用?我无法通过 google 和互联网课程解决我的问题。可能因为我不明白根本问题是什么。
您似乎没有在组件扫描中添加测试包 ru.testproject.db.hibernate.repository。
感谢大家的帮助! 我已经通过以下更改解决了我的问题: 1) 首先 - 我更新了 spring-context 依赖更新版本:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
它解决了 java.lang.NoClassDefFoundError: org/springframework/context/index/CandidateComponentsIndexLoader 错误。 CandidateComponentsIndexLoader 出现在 spring-context 5.0.0.
之后2) 我添加了
@EnableJpaRepositories(basePackages = {
"ru.testproject.hibernate"
})
@PropertySource("classpath:testproject.properties")
@ComponentScan("ru.testproject.hibernate")
注释。以前的更改不会影响任何积极的变化。 1) 它导致 entityManagerFactory 错误,我可以用以下方法解决:
3) 使用
为 entityManagerFactory 设置 packgeToScanentityManagerFactoryBean.setPackagesToScan("ru.testproject.hibernate.entity");
带有实体路径(在这种情况下为用户)。
就是这样。现在存储库工作完美。谢谢大家!!!
ps: 可能是,现在我可以用 spring boot)