Отсутствует DatabsaeClient в приложении Postgres с весенней загрузкой R2dbc
У меня появляется следующая ошибка:
Exception: Error creating bean with name 'inventoryService' defined in URL [jar:file:/app.jar!/BOOT-INF/classes!/com/epi/services/inventory/items/InventoryService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'itemRepository': Cannot resolve reference to bean 'databaseClient' while setting bean property 'databaseClient'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'databaseClient' available
2019-06-18 18:38:41,409 INFO [main] org.apache.juli.logging.DirectJDKLog: Stopping service [Tomcat]
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.catalina.loader.WebappClassLoaderBase (jar:file:/app.jar!/BOOT-INF/lib/tomcat-embed-core-8.5.29.jar!/) to field java.lang.Thread.threadLocals
WARNING: Please consider reporting this to the maintainers of org.apache.catalina.loader.WebappClassLoaderBase
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
2019-06-18 18:38:45,424 INFO [main] org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-06-18 18:38:50,695 ERROR [main] org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.epi.services.inventory.items.InventoryService required a bean named 'databaseClient' that could not be found.
Action:
Consider defining a bean named 'databaseClient' in your configuration.
Мое приложение имеет следующие классы и зависимости:
Внутри основного модуля:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
</dependency>
<dependency>
<groupId>myGroupId</groupId>
<artifactId>myModule.dblib</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectreactor</groupId>
<artifactId>reactor-spring</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
</dependencies>
Приложение:
@SpringBootApplication(scanBasePackages = "com.pack")
@EntityScan("com.pack")
@EnableR2dbcRepositories
@Import(DatabaseConfiguration.class)
public class InventoryApplication {
public static void main(String[] args) {
SpringApplication.run(InventoryApplication.class, args);
}
}
Обслуживание:
@Service
@RequiredArgsConstructor
public class InventoryService {
private final ItemRepository itemRepository;
public Flux<ItemPojo> getAllItems() {
return itemRepository.findAllItems()
.map(Item::toPojo);
}
}
Repo:
@Repository
public interface ItemRepository extends ReactiveCrudRepository<Item, Long> {
Flux<List<Item>> findByName(String name);
@Query("select i from Item i")
Flux<Item> findAllItems();
}
Сущность:
@Data
@Table(name = "items")
public class Item implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public static ItemPojo toPojo(final Item items) {
return new ItemPojo(items.id, items.name);
}
}
myModule.dblib:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectreactor</groupId>
<artifactId>reactor-spring</artifactId>
</dependency>
</dependencies>
Конфигурация базы данных:
@Configuration
@EnableR2dbcRepositories
public class DatabaseConfiguration {
@Value("${spring.data.postgres.host}") private String host;
@Value("${spring.data.postgres.port}") private int port;
@Value("${spring.data.postgres.database}") private String database;
@Value("${spring.data.postgres.username}") private String username;
@Value("${spring.data.postgres.password}") private String password;
@Bean
public PostgresqlConnectionFactory connectionFactory() {
return new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder()
.host(host)
.port(port)
.database(database)
.username(username)
.password(password)
.build());
}
}
Что мне не хватает?
3 ответа
Я собираюсь бесстыдно добавить мою собственную статью о том, как начать работать с R2DBC postgres и весенней загрузкой.
Я думаю, что ваша проблема заключается в том, как вы инициируете вашу базу данных ConnectionFactory. Согласно документации вам нужно расширить и переопределить AbstractR2dbcConfiguration#connectionFactory
@Configuration
@EnableR2dbcRepositories
public class PostgresConfig extends AbstractR2dbcConfiguration {
@Override
@Bean
public ConnectionFactory connectionFactory() {
return new PostgresqlConnectionFactory(
PostgresqlConnectionConfiguration.builder()
.host("localhost")
.port(5432)
.username("postgres")
.password("mysecretpassword")
.database("myDatabase")
.build());
}
}
Официальная документация
Этот подход позволяет использовать стандартный экземпляр io.r2dbc.spi.ConnectionFactory с контейнером, использующим Spring AbstractR2dbcConfiguration.
По сравнению с прямой регистрацией экземпляра ConnectionFactory поддержка конфигурации имеет дополнительное преимущество, заключающееся в предоставлении контейнеру реализации ExceptionTranslator, которая преобразует исключения R2DBC в исключения в переносимой иерархии DataAccessException Spring для классов доступа к данным, аннотированных аннотацией @Repository.
Эта иерархия и использование @Repository описаны в функциях поддержки DAO Spring.
AbstractR2dbcConfiguration регистрирует также DatabaseClient, который необходим для взаимодействия с базой данных и для реализации репозитория.
Используя подход TDD, я пытался заставить работать следующий метод TestNG:
@Test
@Slf4j
@SpringBootTest
class PostgresSanityTesting extends AbstractTestNGSpringContextTests{
@Autowired
DatabaseClient databaseClient
void sanityCheck() {
assert databaseClient
}
Но чтобы попасть туда, есть работа...
Во-первых, я не вижу никакого эффективного кода, если я не заставляю свой подпроект gradle собираться с
ext['spring.version'] = '5.2.0.M2'
который фактически вызван в документации R2Dbc. И половина других зависимостей проистекает из весенних экспериментов и вех. Так что "покупатель остерегается"... (например, я все еще не вижу, чтобы R2dbc правильно обрабатывал сохранение в репозитории () и ряд других проблем.)
Что касается проблемы мгновенного кодирования:
При правильной настройке клиент базы данных является @Bean, настроенным как часть класса AbstractR2dbcConfiguration, и затем этот тест TestNg пройдет.
Но я обнаружил, что мне все еще нужно создать @Bean, и я внедряю параметры фабрики соединений через @ConfigurationProperties. Я разделяю эти проблемы в двух файлах, так как размещение @Bean с подклассом AbstractR2dbcConfiguration просто не работает для меня.
Вот код, который я использую, выраженный в Groovy.
import io.r2dbc.spi.ConnectionFactory
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories
@Configuration
class R2DbcConfiguration extends AbstractR2dbcConfiguration {
private final ConnectionFactory connectionFactory
R2DbcConfiguration( ConnectionFactory connectionFactory ) {
this.connectionFactory = connectionFactory
}
@Override
ConnectionFactory connectionFactory() {
this.connectionFactory
}
}
а также
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration
import io.r2dbc.postgresql.PostgresqlConnectionFactory
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "spring.datasource")
class PostgresConnectionConfig{
String database
String username
String password
String host
String port
@Bean
PostgresqlConnectionFactory connectionFactory() {
final def dbPort = port as Integer
PostgresqlConnectionConfiguration config = PostgresqlConnectionConfiguration.builder() //
.host(host)
.port(dbPort)
.database(database)
.username(username)
.password(password).build()
PostgresqlConnectionFactory candidate = new PostgresqlConnectionFactory(config)
candidate
}
}
и часть моего application.yml
spring:
datasource:
url: jdbc:postgresql://127.0.0.1:5432/postgres
driver-class-name: org.postgresql.Driver
driverClassName: org.postgresql.Driver
database: postgres
host: localhost
port: 5432
password: pokey0
username: postgres
и выдержки из build.gradle моего подпроекта
repositories {
maven { url "https://repo.spring.io/libs-milestone" }
}
dependencies {
implementation group: 'org.springframework.data', name: 'spring-data-releasetrain', version: 'Lovelace-RELEASE', ext: 'pom'
implementation group: 'org.springframework.data', name: 'spring-data-r2dbc', version: '1.0.0.M2'
implementation group: 'io.r2dbc', name: 'r2dbc-postgresql', version: '1.0.0.M7'
implementation('org.springframework.boot:spring-boot-starter-webflux')
implementation('io.projectreactor:reactor-test')
implementation group: 'org.postgresql', name: 'postgresql', version: '42.2.5'
implementation('org.springframework.boot:spring-boot-starter-actuator')
testImplementation('org.springframework.boot:spring-boot-starter-test')
implementation group: 'javax.persistence', name: 'javax.persistence-api', version: '2.2'
}
Оказывается, мне нужно расширить класс конфигурации базы данных AbstractR2dbcConfiguration
который включает в себя DatabaseClient
боб.