Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,10 @@ Full CRUD for users, roles, and permissions, still inside `core/auth`. Assignmen

### Tasks

- [ ] `core/customer`: `Customer` entity (`@Table("customers")`) and `CustomerDto`, `CustomerRowMapper` (entity) plus `CustomerMapper` (entity ↔ dto), `CustomerDao` + impl, `CustomerService` + impl, `DaoConfig`/`ServiceConfig`
- [ ] `ws`: `CustomerController`, `CustomerConverter`
- [ ] `WebMvcConfig` updated to import `core/customer`'s config
- [ ] Tests at all five layers, `CustomerDao` with its own fixture dataset
- [x] `core/customer`: `Customer` entity (`@Table("customers")`) and `CustomerDto`, `CustomerRowMapper` (entity) plus `CustomerMapper` (entity ↔ dto), `CustomerDao` + impl, `CustomerService` + impl, `DaoConfig`/`ServiceConfig`
- [x] `ws`: `CustomerController`, `CustomerConverter`
- [x] `WebMvcConfig` updated to import `core/customer`'s config
- [x] Tests at all five layers, `CustomerDao` with its own fixture dataset

## feature/order

Expand Down
17 changes: 17 additions & 0 deletions core/customer/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,22 @@
<groupId>com.edgareldy.springjdbctutorial</groupId>
<artifactId>common</artifactId>
</dependency>
<!-- Test scope only: the DAO tests reuse the PostgreSQL Testcontainer support published by common -->
<dependency>
<groupId>com.edgareldy.springjdbctutorial</groupId>
<artifactId>common</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.edgareldy.springjdbctutorial.core.customer.config;

import com.edgareldy.springjdbctutorial.core.customer.dao.CustomerDao;
import com.edgareldy.springjdbctutorial.core.customer.dao.impl.CustomerDaoImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

/**
* Declares the DAO beans of the customer module, typed by interface (no stereotype on the implementations).
* <p>
* Created edgar.muhamyangabo on 9/20/26
* Author : edgar.muhamyangabo
* Date : 9/20/26
* Project : spring-jdbc-tutorial
*/
@Configuration
public class DaoConfig {

@Bean
public CustomerDao customerDao(JdbcTemplate jdbcTemplate) {
return new CustomerDaoImpl(jdbcTemplate);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.edgareldy.springjdbctutorial.core.customer.config;

import com.edgareldy.springjdbctutorial.core.customer.dao.CustomerDao;
import com.edgareldy.springjdbctutorial.core.customer.service.CustomerService;
import com.edgareldy.springjdbctutorial.core.customer.service.impl.CustomerServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
* Declares the service beans of the customer module. Imports its DaoConfig so importing this class alone is enough.
* <p>
* Created edgar.muhamyangabo on 9/20/26
* Author : edgar.muhamyangabo
* Date : 9/20/26
* Project : spring-jdbc-tutorial
*/
@Configuration
@Import(DaoConfig.class)
public class ServiceConfig {

@Bean
public CustomerService customerService(CustomerDao customerDao) {
return new CustomerServiceImpl(customerDao);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.edgareldy.springjdbctutorial.core.customer.dao;

import com.edgareldy.springjdbctutorial.core.customer.entity.Customer;

import java.util.List;
import java.util.Optional;

/**
* Data access for customers. Entity types only.
* <p>
* Created edgar.muhamyangabo on 9/20/26
* Author : edgar.muhamyangabo
* Date : 9/20/26
* Project : spring-jdbc-tutorial
*/
public interface CustomerDao {

/** One page of customers ordered by id. */
List<Customer> findPage(int page, int size);

long countAll();

Optional<Customer> findById(Long id);

/** Inserts the customer and returns it with its generated id. */
Customer insert(Customer customer);

/** Returns the number of rows affected, 0 when the customer no longer exists. */
int update(Customer customer);

void delete(Long id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.edgareldy.springjdbctutorial.core.customer.dao.impl;

import com.edgareldy.springjdbctutorial.core.common.dao.support.AbstractDao;
import com.edgareldy.springjdbctutorial.core.customer.dao.CustomerDao;
import com.edgareldy.springjdbctutorial.core.customer.entity.Customer;
import com.edgareldy.springjdbctutorial.core.customer.mapper.CustomerRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;
import java.util.Optional;

/**
* JdbcTemplate implementation of CustomerDao. Declared by @Bean in DaoConfig, no stereotype.
* <p>
* Created edgar.muhamyangabo on 9/20/26
* Author : edgar.muhamyangabo
* Date : 9/20/26
* Project : spring-jdbc-tutorial
*/
public class CustomerDaoImpl extends AbstractDao implements CustomerDao {

private static final String COLUMNS = "id, first_name, last_name, telephone, email, address";

private final CustomerRowMapper rowMapper = new CustomerRowMapper();

public CustomerDaoImpl(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}

@Override
public List<Customer> findPage(int page, int size) {
return getJdbcTemplate().query(
"SELECT " + COLUMNS + " FROM customers ORDER BY id LIMIT ? OFFSET ?",
rowMapper, size, (long) page * size);
}

@Override
public long countAll() {
return count("SELECT COUNT(*) FROM customers");
}

@Override
public Optional<Customer> findById(Long id) {
return findOne("SELECT " + COLUMNS + " FROM customers WHERE id = ?", rowMapper, id);
}

@Override
public Customer insert(Customer customer) {
long id = insertReturningId(
"INSERT INTO customers (first_name, last_name, telephone, email, address) "
+ "VALUES (?, ?, ?, ?, ?) RETURNING id",
customer.getFirstName(), customer.getLastName(), customer.getTelephone(),
customer.getEmail(), customer.getAddress());
customer.setId(id);
return customer;
}

@Override
public int update(Customer customer) {
return getJdbcTemplate().update(
"UPDATE customers SET first_name = ?, last_name = ?, telephone = ?, email = ?, address = ? WHERE id = ?",
customer.getFirstName(), customer.getLastName(), customer.getTelephone(),
customer.getEmail(), customer.getAddress(), customer.getId());
}

@Override
public void delete(Long id) {
getJdbcTemplate().update("DELETE FROM customers WHERE id = ?", id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.edgareldy.springjdbctutorial.core.customer.dto;

import java.util.Objects;

/**
* Business shape of a customer.
* <p>
* Created edgar.muhamyangabo on 9/20/26
* Author : edgar.muhamyangabo
* Date : 9/20/26
* Project : spring-jdbc-tutorial
*/
public class CustomerDto {

private Long id;

private String firstName;

private String lastName;

private String telephone;

private String email;

private String address;

public CustomerDto() {
}

public CustomerDto(Long id, String firstName, String lastName, String telephone, String email, String address) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.telephone = telephone;
this.email = email;
this.address = address;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getTelephone() {
return telephone;
}

public void setTelephone(String telephone) {
this.telephone = telephone;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CustomerDto that = (CustomerDto) o;
return Objects.equals(id, that.id) && Objects.equals(firstName, that.firstName)
&& Objects.equals(lastName, that.lastName) && Objects.equals(telephone, that.telephone)
&& Objects.equals(email, that.email) && Objects.equals(address, that.address);
}

@Override
public int hashCode() {
return Objects.hash(id, firstName, lastName, telephone, email, address);
}

@Override
public String toString() {
return "CustomerDto{" +
"id=" + id +
", firstName=" + firstName +
", lastName=" + lastName +
", telephone=" + telephone +
", email=" + email +
", address=" + address +
"}";
}
}
Loading
Loading