How to set up a PostgreSQL database connection in r2dbc Spring boot project?
I have tried the below configuration, it connects to the database but it's not returning any values
#Configuration
#EnableR2dbcRepositories
public class DatabaseConfig extends AbstractR2dbcConfiguration {
#Override
public ConnectionFactory connectionFactory() {
return ConnectionFactories.get("r2dbc:postgresql://localhost:5432/sample");
}
/*#Override
public ConnectionFactory connectionFactory() {
return ConnectionFactories.get(new PostgresqlConnectionFactory(
PostgresqlConnectionConfiguration.builder()
.host("localhost")
.port(5432)
.username("postgres")
.password("thirumal")
.database("sample")
.build()););
}*/
}
application.properties
spring.r2dbc.url=r2dbc:postgresql://localhost:5432/sample
spring.r2dbc.username=postgres
spring.r2dbc.password=thirumal
spring.r2dbc.pool.enabled=true
Model
#Data#NoArgsConstructor#AllArgsConstructor#Getter#Setter
#ToString
#Table("public.test")
public class Test implements Serializable{
/**
*
*/
private static final long serialVersionUID = 4205798689305488147L;
#Id//#Column("id")
private Long id;
private String name;
}
Repository
public interface TestRepository extends ReactiveCrudRepository<Test, Long> {
}
REST CONTROLLER:
#GetMapping("/test")
public Mono<Test> test() {
testRepository.findById(3L).subscribe(v->System.out.println("Value: " + v.toString()));
return testRepository.findById(3L);
}
It prints the output in the console but in the JSON, I get only empty braces {}
What is the correct way to configure? Any other configuration is required?
I found the problem. It's Lombok library, I didn't install it in eclipse.
When I created the getter and setter method manually it worked.
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Then, I set up the lombok and used #getter and #setter and it worked.
This configuration works for me, but I use the DatabaseClient instead of the R2dbcRepositories to query the data:
#Configuration
public class DatabaseConfiguration extends AbstractR2dbcConfiguration {
#Override
#Bean
public ConnectionFactory connectionFactory() {
return new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder()
.host("localhost")
.port(5432)
.username("username")
.password("password")
.database("mydb")
.build());
}
}
Then in the repository:
#Repository
public class MyRepository {
#Autowired
private DatabaseClient client;
public Flux<String> getString() {
....
}
}
UPDATE:
If it's connect to the database probably your configuration is right, can you share also the code used to get the data?
It's possible that you are getting the result as Mono or Flux, but not reading from it (try with subscribe()).
Mono<String> mono = db.getData();
mono.subscribe(value -> System.out.println(value));
Related
I'm a bit new to Spring Boot and I'm trying to create model/repo/service/serviceImp/controller type of architecture.
After I try to make a this get request:
http://localhost:8080/api/v1/people/name?name=steve
and I get this error (I created a couple of people in DB):
"java.lang.NullPointerException: Cannot invoke \"com.project.Springbootbackend.service.PeopleService.findAllByName(String)\" because \"this.peopleService\" is null\r\n\tat com.project.Springbootbackend.controller.PeopleController.findAllByName(PeopleController.java:24)
This is my code:
People(entity)
#Entity
public class People {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#Column(name = "email")
private String email;
...
//constructor + get/set
PeopleController
#RestController
#RequestMapping("/api/v1/people")
#RequiredArgsConstructor
public class PeopleController {
private PeopleService peopleService;
#GetMapping("/name")
public ResponseEntity<List<People>> findAllByName(#RequestParam String name) {
return ResponseEntity.ok().body(peopleService.findAllByName(name));
}
}
PeopleRepo
public interface PeopleRepository extends JpaRepository<People, Integer> {
List<People> findAllByName(String name);
}
PeopleService
public interface PeopleService {
List<People> findAllByName(String name);
}
PeopleServiceImp
#RequiredArgsConstructor
#Service
public class PeopleServiceImp implements PeopleService {
PeopleRepository peopleRepository;
#Override
public List findAllByName(String name) {
return (List) ResponseEntity.ok(peopleRepository.findAllByName(name));
}
}
Thx guys in advance.
*SOLUTION:
Entity, service & repository is the same.
ServiceImp and controller changes are down belowe:
Controller:
#RestController
#RequestMapping("/api/v1/people")
public class PeopleController {
private PeopleService peopleService;
public PeopleController(PeopleService peopleService) {
this.peopleService = peopleService;
}
#GetMapping("/name")
public ResponseEntity<List<People>> findAllByName(#RequestParam String name) {
return ResponseEntity.ok().body(peopleService.findAllByName(name));
}
}
ServiceImp
#Service
public class PeopleServiceImp implements PeopleService {
private PeopleRepository peopleRepository;
public PeopleServiceImp(PeopleRepository peopleRepository) {
this.peopleRepository = peopleRepository;
}
#Override
public List<People> findAllByName(String name) {
List<People> people = peopleRepository.findAllByName(name);
return people;
}
}
Your constructor does not inject the service, because of the RequiredArgsConstructor (see Link) needs special treatment. Therefore, use final:
#RestController
#RequestMapping("/api/v1/people")
#RequiredArgsConstructor
public class PeopleController {
private final PeopleService peopleService;
#GetMapping("/name")
public ResponseEntity<List<People>> findAllByName(#RequestParam String name) {
return ResponseEntity.ok().body(peopleService.findAllByName(name));
}
}
Same here:
#RequiredArgsConstructor
#Service
public class PeopleServiceImp implements PeopleService {
private final PeopleRepository peopleRepository;
#Override
public List findAllByName(String name) {
return (List) ResponseEntity.ok(peopleRepository.findAllByName(name));
}
}
Additional hint, use a typed list:
#Override
public List<People> findAllByName(String name) {
return ResponseEntity.ok(peopleRepository.findAllByName(name));
}
Try like this:
#Autowired
private PeopleService peopleService;
#Autowired
private PeopleRepository peopleRepository;
You also need to add the #SpringBootApplication annotation in the main class of the application.
Something like that:
#SpringBootApplication
class PeopleApplication {
public static void main(String[] args) {
...
Take a look at this article about automatic dependency injection in Spring:
https://www.baeldung.com/spring-autowire
You missed the autowiring annotation in the controller to inject the service which may make this.peopleService to be null.
#Autowired
private PeopleService peopleService;
You also need to do autowire in your serviceimpl class
#Autowired
private PeopleRepository peopleRepository;
I am a fish in Spring Boot and Data Jpa, I Tried to create a basic Spring boot application but every time I am encountering the error. Can you help me?
That's my code:
Spring Boot Application class:
#SpringBootApplication
#ComponentScan(basePackages = "com.project.*")
#EnableJpaRepositories(basePackages = "com.project.repository.*")
#EntityScan(basePackages = "com.project.entities.*")
#EnableAutoConfiguration
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
Controller Class:
#RestController
#RequestMapping(value = "/api")
public class controller {
private IUserServices userServices;
#Autowired
public controller(IUserServices userServices) {
this.userServices = userServices;
}
#GetMapping(value = "/merhaba")
public String sayHello(){
return "Hello World";
}
#GetMapping(value = "/getall")
public List<User> getAll(){
return this.userServices.getAllUsers();
}
}
Repository Class:
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
}
IServices Class:
#Service
public interface IUserServices {
void saveUser(User user);
List<User> getAllUsers();
}
ServicesImpl Class:
#Service
public class UserServicesImpl implements IUserServices{
private UserRepository userRepository;
#Autowired
public UserServicesImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public void saveUser(User user) {
this.userRepository.save(user);
}
#Override
public List<User> getAllUsers() {
return this.userRepository.findAll();
}
}
Entity Class:
#Entity
#Table(catalog = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
#Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
AND THIS MY ERROR MESSAGE:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.project.services.UserServicesImpl required a bean of
type 'com.project.repository.UserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.project.repository.UserRepository' in your
configuration.
Process finished with exit code 0
SO This is application properties file:
spring.jpa.properties.hibernate.dialect =
org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.show-sql=true
spring.datasource.url=jdbc:postgresql://localhost:5432/u
spring.datasource.username=postgres
spring.datasource.password=1234
spring.jpa.properties.javax.persistence.validation.mode = none
There are some issues that you should fix them.
First
When you have the spring boot application with #SpringBootApplication you don't need other stuff such as #EnableAutoConfiguration and etc, So remove them all.
You can read more about it here.
Second
You don't need to annotate your service interface with #Service, because you did it in the UserServicesImpl class.
Third
You defined id as an integer in your user entity but in the repository, you wrote your id as Long. It's wrong. It should be something like this.
#Repository
public interface UserRepository extends JpaRepository<User,Integer> {
}
Try the above solutions and let me know the result.
I am new to spring.
I just tried successfully using an entity class without #Id in Spring Data JDBC
Custom query was added in my repository for retrieving data from 2 mysql tables and returning an entity having the joined table data.
If I plan to use only custom queries, am I missing anything here?
Here's my entity class without #Id or #Entity:
public class Item
{
private long id;
private String code;
private String itemName;
private String groupName;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
}
Repository layer:
#Repository
public interface ItemRepository extends PagingAndSortingRepository<Item, Long>
{
#Query("SELECT a.id, a.code, a.name AS item_name,
b.name as group_name from item a, item_group b
WHERE a.group_id = b.id AND a.id=:id")
Item findItemById(#Param("id") Long id);
}
Service layer:
#Service
public class ItemServiceImpl implements ItemService
{
private final ItemRepository itemRepository;
public ItemServiceImpl(ItemRepository itemRepository)
{
this.itemRepository = itemRepository;
}
#Override
#Transactional(readOnly=true)
public Item findItemById(Long id)
{
return itemRepository.findItemById(id);
}
}
My updated main Configuration class in response to answer of Jens:
#SpringBootApplication
#EnableJdbcRepositories
public class SpringDataJdbcApplication extends AbstractJdbcConfiguration
{
public static void main(String[] args)
{
SpringApplication.run(SpringDataJdbcApplication.class, args);
}
#Bean
#ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource()
{
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
return dataSourceBuilder.build();
}
#Bean
NamedParameterJdbcOperations namedParameterJdbcOperations(DataSource dataSource)
{
return new NamedParameterJdbcTemplate(dataSource);
}
#Bean
PlatformTransactionManager transactionManager()
{
return new DataSourceTransactionManager(dataSource());
}
}
If you don't get any exceptions you should be fine. There shouldn't be anything in Spring Data JDBC that silently breaks when the id is not specified.
The problem is though: I don't consider it a feature that this works, but just accidental behaviour. This means it might break with any version, although replacing these methods with custom implementations based on a NamedParameterJdbcTemplate shouldn't be to hard, so the risk is limited.
The question though is: Why don't you add the #Id annotation, after all your entity does have an id. And the whole idea of a repository conceptually requires an id.
If it's working and you really don't want to use the annotations, you can do it. But I think that it's unnecessary complication. You can expect errors that would not be there if you had used the annotations and code will be harder to debug. If you are new in Spring I recommend to use annotations. But after all it depend on you how will you design your applications. For sure advantage of approach without annotations is higher control about database.
I have to write some junit test cases to check entity. I'm using postgres as my database.
My entity class
#Entity
#Table(name = "display")
public class Display {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String group;
public Display() {
}
public Display(Long id, String title, String grp) {
this.id = id;
this.title= title;
this.group= grp;
}
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return this.id;
}
public void setGroup(String id) {
this.group = id;
}
public String getGroup() {
return this.group;
}
public void settitle(String title) {
this.title = title;
}
public String gettitle() {
return this.title;
}
}
My repository
#Repository
public interface DisplayRepository extends CrudRepository<Display, Long> {
}
Interface
public interface IDisplayService {
List<Display> findAll();
}
Service class
#Service
public class DisplayService implements IDisplayService {
#Autowired
private DisplayRepository repository;
#Override
public List<Display> findAll() {
List<Display> d = (List<Display>) repository.findAll();
return d;
}
}
I tried writing junit test cases but I get Could'nt load Application. Whats the right way to write junit test cases for this?
This is the test case I wrote for service
folder : test/java/example/demo/Test.java
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource("classpath:conn.properties")
public class DisplayServiceTest {
#Value("${id}")
private String value;
#Mock
private DisplayRepository DisplayReps;
#InjectMocks
private DisplayService DisplayService;
#Test
public void whenFindAll_thenReturnProductList() {
Menu m = new Menu()
m.setId(value);
List<Display> expectedDisplay = Arrays.asList(m);
doReturn(expectedDisplay).when(DisplayReps).findAll();
List<Display> actualDisplay = DisplayService.findAll();
assertThat(actualDisplay).isEqualTo(expectedDisplay);
}
in test/java/example/demo/resources
conn.properties
id=2
Its returning 0 for value
Whats the issue?
Thanks
I have managed to make your code to work. I will post only the changed classes:
The interface:
public interface DisplayRepository extends CrudRepository<Display, Long> {
Optional<Display> findByTitle(String name);
}
The test class:
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class DisplayRepositoryTest {
#Autowired
private TestEntityManager testEntityManager;
#Autowired
private DisplayRepository productRespository;
#Before()
public void setUp(){
Display m = new Display();
// m.setId(2L); // The ID is autogenerated; can retrieve it from the persistAndFlush result
m.setCategory("Group1");
m.setTitle("Product2");
testEntityManager.persistAndFlush(m);
}
#Test
public void whenFindByName_thenReturnProduct() {
// when
Display product = productRespository.findByTitle("Product2").orElseThrow(() -> new RuntimeException("Product not found"));
// then
assertThat(product.getTitle()).isEqualTo("Product2");
}
#Test
public void whenFindAll_thenReturnProductList() {
// when
List<Display> products = (List<Display>) productRespository.findAll();
// then
assertThat(products).hasSize(1);
}
}
When trying to run the code you provided, there were a few issues:
you were using the reserved word group as a field in the Display class. Because of this, Hibernate couldn't create the table, so I renamed it to category.
there was a compilation issue because the method findByName wasn't defined in the repository; also, there was no field name in the Display class to which the mapping to be made; because of this, I've added the method findByTitle because it's an existing field and it seemed to match the value you queried in the test method.
because the ID field is autogenerated, the test setup() failed when persisting the Display.
If you want to use #Mock for mocking classes, you must call:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
You can then mock responses as usual: Mockito.when(DisplayReps.findByTitle("A")).thenReturn(Optional.of(new Display(2L, "ALFA", "GRP1")));
I have following Spring Boot sample application.
The crazy thing is if I add #EnableMongoAuditing annotation on SampleApplication bean, lastModifiedDate would be filled by createDate would not. Why is that? I searched the web and many people had problems on emptying createDate during an update, but I don't have an update.
Document class:
#Document
public class SampleBean implements Persistable<String> {
#Id
public String id;
#CreatedDate
public LocalDateTime createDate;
#LastModifiedDate
public LocalDateTime lastModifiedDate;
public String name;
#Override
public String getId() {
return id;
}
#Override
public boolean isNew() {
return id != null;
}
}
Repository Interface:
#Repository
public interface SampleBeanRepository extends MongoRepository<SampleBean, String> {
}
Rest Controller:
#RestController
public class WebService {
#Autowired
private SampleBeanRepository repository;
#RequestMapping("/insert")
public String insert() {
SampleBean sampleBean = new SampleBean();
sampleBean.name = "Prefix" + new Random().nextInt(1000);
repository.insert(sampleBean);
return "done";
}
#RequestMapping("/")
public Collection<SampleBean> home() {
return repository.findAll();
}
}
Application Config:
#SpringBootApplication
#EnableMongoAuditing
public class ApplicationConfig {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
Your isNew() strategy is the culprit here. Since you have set condition as id != null. Everytime your SampleBean is created there will be no id set as per your code snippet, the isNew() method will return as false hence only LastModifiedDate will be set by the framework. Either change the isNew() method condition to return id == null; or just don't implement Persistable interface whatever default strategy for isNew will be picked.