[BAEL-6275] PostgreSQL NOTIFY/LISTEN (#14153)
* [BAEL-4849] Article code * [BAEL-4968] Article code * [BAEL-4968] Article code * [BAEL-4968] Article code * [BAEL-4968] Remove extra comments * [BAEL-5258] Article Code * [BAEL-2765] PKCE Support for Secret Clients * [BAEL-5698] Article code * [BAEL-5698] Article code * [BAEL-5905] Initial code * [BAEL-5905] Article code * [BAEL-5905] Relocate article code to new module * [BAEL-6275] PostgreSQL NOTIFY/LISTEN * [BAEL-6275] Minor correction --------- Co-authored-by: Philippe Sevestre <psevestre@gmail.com>
This commit is contained in:
parent
2e023a6ba2
commit
70acdb27ee
|
@ -22,6 +22,7 @@
|
||||||
<module>spring-amqp</module>
|
<module>spring-amqp</module>
|
||||||
<module>spring-apache-camel</module>
|
<module>spring-apache-camel</module>
|
||||||
<module>spring-jms</module>
|
<module>spring-jms</module>
|
||||||
|
<module>postgres-notify</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -0,0 +1 @@
|
||||||
|
/application-local.properties
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>postgres-notify</artifactId>
|
||||||
|
<name>postgres-notify</name>
|
||||||
|
<description>PostgreSQL as a Message Broker</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>messaging-modules</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jdbc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
</properties>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>instance1</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<jvmArguments>-Dserver.port=8081</jvmArguments>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
</profiles>
|
||||||
|
</project>
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.messaging.postgresql;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.baeldung.messaging.postgresql.config;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||||
|
import org.springframework.cache.support.SimpleCacheManager;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.util.ConcurrentLruCache;
|
||||||
|
|
||||||
|
import com.baeldung.messaging.postgresql.domain.Order;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class CacheConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Cache ordersCache(CacheManager cm) {
|
||||||
|
return cm.getCache("orders");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
CacheManager defaultCacheManager() {
|
||||||
|
SimpleCacheManager cm = new SimpleCacheManager();
|
||||||
|
Cache cache = new ConcurrentMapCache("orders",false);
|
||||||
|
cm.setCaches(Arrays.asList(cache));
|
||||||
|
|
||||||
|
return cm;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.baeldung.messaging.postgresql.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import com.baeldung.messaging.postgresql.service.NotifierService;
|
||||||
|
import com.baeldung.messaging.postgresql.service.NotificationHandler;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
public class ListenerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
CommandLineRunner startListener(NotifierService notifier, NotificationHandler handler) {
|
||||||
|
return (args) -> {
|
||||||
|
log.info("Starting order listener thread...");
|
||||||
|
Runnable listener = notifier.createNotificationHandler(handler);
|
||||||
|
Thread t = new Thread(listener, "order-listener");
|
||||||
|
t.start();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.baeldung.messaging.postgresql.config;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
|
import com.baeldung.messaging.postgresql.service.NotifierService;
|
||||||
|
import com.zaxxer.hikari.util.DriverDataSource;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class NotifierConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
NotifierService notifier(DataSourceProperties props) {
|
||||||
|
|
||||||
|
DriverDataSource ds = new DriverDataSource(
|
||||||
|
props.determineUrl(),
|
||||||
|
props.determineDriverClassName(),
|
||||||
|
new Properties(),
|
||||||
|
props.determineUsername(),
|
||||||
|
props.determinePassword());
|
||||||
|
|
||||||
|
JdbcTemplate tpl = new JdbcTemplate(ds);
|
||||||
|
|
||||||
|
return new NotifierService(tpl);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.baeldung.messaging.postgresql.controller;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.baeldung.messaging.postgresql.domain.Order;
|
||||||
|
import com.baeldung.messaging.postgresql.domain.OrderType;
|
||||||
|
import com.baeldung.messaging.postgresql.service.OrdersService;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class OrdersController {
|
||||||
|
|
||||||
|
private final OrdersService orders;
|
||||||
|
|
||||||
|
@PostMapping("/orders/sell")
|
||||||
|
public ResponseEntity<Order> postSellOrder(String symbol, BigDecimal quantity, BigDecimal price) {
|
||||||
|
log.info("postSellOrder: symbol={},quantity={},price={}", symbol,quantity,price);
|
||||||
|
Order order = orders.createOrder(OrderType.SELL, symbol, quantity, price);
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/orders/buy")
|
||||||
|
public ResponseEntity<Order> postBuyOrder(String symbol, BigDecimal quantity, BigDecimal price) {
|
||||||
|
log.info("postBuyOrder: symbol={},quantity={},price={}", symbol,quantity,price);
|
||||||
|
Order order = orders.createOrder(OrderType.BUY, symbol, quantity, price);
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/orders/{id}")
|
||||||
|
public ResponseEntity<Order> getOrderById(@PathVariable Long id) {
|
||||||
|
|
||||||
|
Optional<Order> o = orders.findById(id);
|
||||||
|
if (o.isEmpty()) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok(o.get());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.baeldung.messaging.postgresql.domain;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.relational.core.mapping.Table;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ToString
|
||||||
|
@Table(name = "orders")
|
||||||
|
public class Order {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
private String symbol;
|
||||||
|
private OrderType orderType;
|
||||||
|
private BigDecimal price;
|
||||||
|
private BigDecimal quantity;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.messaging.postgresql.domain;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum OrderType {
|
||||||
|
BUY('B'),
|
||||||
|
SELL('S');
|
||||||
|
private final char c;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.baeldung.messaging.postgresql.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
import com.baeldung.messaging.postgresql.domain.Order;
|
||||||
|
|
||||||
|
public interface OrdersRepository extends CrudRepository<Order, Long>{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.baeldung.messaging.postgresql.service;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.postgresql.PGNotification;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.baeldung.messaging.postgresql.domain.Order;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class NotificationHandler implements Consumer<PGNotification>{
|
||||||
|
|
||||||
|
private final OrdersService orders;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(PGNotification t) {
|
||||||
|
log.info("Notification received: pid={}, name={}, param={}",t.getPID(),t.getName(),t.getParameter());
|
||||||
|
Optional<Order> order = orders.findById(Long.valueOf(t.getParameter()));
|
||||||
|
if ( !order.isEmpty()) {
|
||||||
|
log.info("order details: {}", order.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.baeldung.messaging.postgresql.service;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.postgresql.PGConnection;
|
||||||
|
import org.postgresql.PGNotification;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.baeldung.messaging.postgresql.domain.Order;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class NotifierService {
|
||||||
|
private static final String ORDERS_CHANNEL = "orders";
|
||||||
|
private final JdbcTemplate tpl;
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void notifyOrderCreated(Order order) {
|
||||||
|
tpl.execute("NOTIFY " + ORDERS_CHANNEL + ", '" + order.getId() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Runnable createNotificationHandler(Consumer<PGNotification> consumer) {
|
||||||
|
|
||||||
|
return () -> {
|
||||||
|
tpl.execute((Connection c) -> {
|
||||||
|
log.info("notificationHandler: sending LISTEN command...");
|
||||||
|
c.createStatement().execute("LISTEN " + ORDERS_CHANNEL);
|
||||||
|
|
||||||
|
PGConnection pgconn = c.unwrap(PGConnection.class);
|
||||||
|
|
||||||
|
while(!Thread.currentThread().isInterrupted()) {
|
||||||
|
PGNotification[] nts = pgconn.getNotifications(10000);
|
||||||
|
if ( nts == null || nts.length == 0 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( PGNotification nt : nts) {
|
||||||
|
consumer.accept(nt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.baeldung.messaging.postgresql.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.baeldung.messaging.postgresql.domain.Order;
|
||||||
|
import com.baeldung.messaging.postgresql.domain.OrderType;
|
||||||
|
import com.baeldung.messaging.postgresql.repository.OrdersRepository;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class OrdersService {
|
||||||
|
private final OrdersRepository repo;
|
||||||
|
private final NotifierService notifier;
|
||||||
|
private final Cache ordersCache;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Order createOrder(OrderType orderType, String symbol, BigDecimal quantity, BigDecimal price) {
|
||||||
|
|
||||||
|
Order order = new Order();
|
||||||
|
order.setOrderType(orderType);
|
||||||
|
order.setSymbol(symbol);
|
||||||
|
order.setQuantity(quantity);
|
||||||
|
order.setPrice(price);
|
||||||
|
order = repo.save(order);
|
||||||
|
|
||||||
|
notifier.notifyOrderCreated(order);
|
||||||
|
|
||||||
|
return order;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Optional<Order> findById(Long id) {
|
||||||
|
Optional<Order> o = Optional.ofNullable(ordersCache.get(id, Order.class));
|
||||||
|
if ( !o.isEmpty() ) {
|
||||||
|
log.info("findById: cache hit, id={}",id);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("findById: cache miss, id={}",id);
|
||||||
|
o = repo.findById(id);
|
||||||
|
if ( o.isEmpty()) {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
ordersCache.put(id, o.get());
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Replace the properties below with proper values for your environment
|
||||||
|
spring.sql.init.mode=ALWAYS
|
||||||
|
#spring.datasource.url=jdbc:postgresql://some-postgresql-host/some-postgresql-database
|
||||||
|
#spring.datasource.username=your-postgresql-username
|
||||||
|
#spring.datasource.password=your-postgresql-password
|
|
@ -0,0 +1,7 @@
|
||||||
|
create table if not exists orders (
|
||||||
|
id serial primary key,
|
||||||
|
symbol varchar(16) not null,
|
||||||
|
order_type varchar(8) not null,
|
||||||
|
price NUMERIC(10,2) not null,
|
||||||
|
quantity NUMERIC(10,2) not null
|
||||||
|
);
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.baeldung.messaging.postgresql;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
|
import com.baeldung.messaging.postgresql.domain.Order;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
|
@Sql("/schema.sql")
|
||||||
|
class ApplicationLiveTest {
|
||||||
|
|
||||||
|
|
||||||
|
@LocalServerPort
|
||||||
|
int localPort;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
TestRestTemplate client;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenCreateBuyOrder_thenSuccess() {
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||||
|
|
||||||
|
MultiValueMap<String, String> data= new LinkedMultiValueMap<>();
|
||||||
|
data.add("symbol", "BAEL");
|
||||||
|
data.add("price", "14.56");
|
||||||
|
data.add("quantity", "100");
|
||||||
|
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(data, headers);
|
||||||
|
|
||||||
|
client.postForEntity("http://localhost:" + localPort + "/orders/buy", data, Order.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
spring.datasource.url=jdbc:postgresql://localhost:5432/baeldung
|
||||||
|
spring.datasource.username=baeldung
|
||||||
|
spring.datasource.password=SqD64PtsGhDXjn9f
|
Loading…
Reference in New Issue