14_Database

Keine Dateien in diesem Thema verfügbar.

Lernmaterialien

Id

PizzaOrder.java

private static final AtomicLong sequence = new AtomicLong(1000);

public void setOrderId() {
    this.orderId = sequence.getAndIncrement();
}
+------------+  +------------+  +------------+  +------------+
|  Client 1  |  |  Client 2  |  |  Client 3  |  |  Client 4  |
|  sequence  |  |  sequence  |  |  sequence  |  |  sequence  |
+------+-----+  +------+-----+  +------+-----+  +------+-----+
       |               |               |               | HTTP request
       +---------------+------+--------+---------------+
                              |
                      +-------+-------+
                      |     Server    |
                      | Spring/Vaadin |
                      |    sequence   |
                      +--------+------+
                               |
                      +--------+------+
                      |    Database   |
                      |   PostgreSQL  |
                      |    sequence   |
                      +---------------+

private static final AtomicLong sequence = new AtomicLong(1000);

public void setOrderId() { this.orderId = sequence.getAndIncrement(); }

setOrderId();

001.png
ALTER SEQUENCE pizza_order_order_id_seq RESTART WITH 1000;

Postgres

docker run --name postgres -e POSTGRES_PASSWORD=postgres  -p 5432:5432 -d postgres
docker start postgres

pom.xml

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

Create database and user

psql "postgresql://postgres:postgres@localhost:5432/"
CREATE USER pizzauser WITH PASSWORD 'secret';
CREATE DATABASE pizzadb OWNER pizzauser;
quit;

Check the connection

psql -U pizzauser -d pizzadb
002.png
003.png

application.properties

# PostgreSQL configuration.
spring.datasource.url=jdbc:postgresql://localhost:5432/pizzadb
spring.datasource.username=pizzauser
spring.datasource.password=secret
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Code

PizzaOrder.java

@Entity
public class PizzaOrder implements Cloneable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long      orderId;

Repository

004.png

PizzaOrderRepository.java

public interface PizzaOrderRepository
        extends JpaRepository<PizzaOrder, Long> {
}
public interface PizzaOrderRepository
        extends JpaRepository<PizzaOrder, Long> {
}
  • This defines a repository interface for PizzaOrder.

  • It extends JpaRepository, so Spring automatically provides database methods.

  • PizzaOrder = entity type

  • Long = type of the ID (orderId)

You automatically get methods like:

findAll()
findById(Long id)
save(PizzaOrder order)
deleteById(Long id)

👉 Interface for database access — no implementation needed, Spring generates it.

Service

Repository

PizzaOrderService.java

@Service
public class PizzaOrderService {
    private ArrayList<PizzaOrder> orders;

private ArrayList<PizzaOrder> orders;

private final PizzaOrderRepository repository;

Main difference:

ArrayList version PostgreSQL version
stores data in RAM stores data in PostgreSQL
data lost when app stops data stays in database
uses orders.add() uses repository.save()
uses orders.clear() uses repository.deleteAll()
searches with loops uses repository.findById()
manually sets IDs database creates IDs

PizzaOrderService.java

@Service
public class PizzaOrderService {
    private final PizzaOrderRepository repository;

    public PizzaOrderService(PizzaOrderRepository repository) {
        this.repository = repository;

        if (repository.count() == 0) {
            fillTestData(500);
        }
    }
public PizzaOrderService(PizzaOrderRepository repository) {

This is the constructor of PizzaOrderService.

Spring sees that the constructor needs a PizzaOrderRepository.

this.repository = repository;

The given repository object is stored in the service field:

private final PizzaOrderRepository repository;

So the service can use it later.

if (repository.count() == 0) {
    fillTestData(500);
}

This checks the database table.

  • count() == 0 → table is empty

  • then fillTestData(500) creates 500 test orders

Where is repository generated?

You write only the interface:

public interface PizzaOrderRepository
        extends JpaRepository<PizzaOrder, Long> {
}

Spring Data JPA automatically creates the real implementation at runtime.

So you do not write:

new PizzaOrderRepository()

Spring does it for you and injects it into the constructor.

In short:

PizzaOrderRepository interface
        |
        v
Spring Data JPA creates implementation
        |
        v
Spring injects it into PizzaOrderService constructor

OneMore

    public void oneMore(Long orderId) {
        if (orderId == null)
            throw new PizzaOrderException("No Order ID!");
        Optional<PizzaOrder> order = repository.findById(orderId);
        if (order.isEmpty())
            throw new PizzaOrderException("Order not found!");
        else
            order.get().setQuantity(order.get().getQuantity()+1);
    }

Now we have to change the implementation from a collection to the database.

Optional<PizzaOrder> order = repository.findById(orderId);

This searches the database for a PizzaOrder with the given orderId.

findById(orderId) returns an Optional because the order may or may not exist.

  • order.isPresent() order was found.

  • order.isEmpty() order was not found

order.get().setQuantity(order.get().getQuantity()+1);

  • order is an Optional<PizzaOrder>
  • order.get() → gets the actual PizzaOrder object (must exist!)
  • getQuantity() → reads current quantity
  • + 1 → increases it by one
  • setQuantity(...) → writes the new value back

increase the order’s quantity by 1

    public PizzaOrder add(PizzaOrder order) {
        return repository.save(order);
    }
  • repository.save(order) → saves the PizzaOrder to the database
    • if it is new → insert
    • if it already exists → update
  • It returns the saved object (including generated values like orderId)

store the order in the database and return it

OrdersView.java

if (existingOrder != null)

        Button saveButton = new Button(OK", event -> {
            if (binder.validate().isOk()) {
                pizzaOrderService.add(order);

Because with JPA this works for both cases:

pizzaOrderService.add(order);

save() means:

  • orderId == null → create new database row
  • orderId exists → update existing database ro

JPA save() handles insert and update automatically.

    public void removePizzaOrder(Long orderId) {
        if (orderId == null) {
            throw new PizzaOrderException("No Order ID!");
        }

        if (!repository.existsById(orderId)) {
            throw new PizzaOrderException(
                    "Order with the ID " + orderId + " not found!"
            );
        }

        repository.deleteById(orderId);
    }

repository.existsById(orderId)

  • true → order exists
  • false → order does not exist

repository.deleteById(orderId);

If an order with this ID exists → it is removed.

If not → depending on setup, it may throw an exception.

    public void removeAll() {
        repository.deleteAll();
    }

repository.deleteAll();

Deletes all PizzaOrder records from the database.

    public void oneMore(Long orderId) {
        if (orderId == null)
            throw new PizzaOrderException("No Order ID!");
        Optional<PizzaOrder> order = repository.findById(orderId);
        if (order.isEmpty()) {
            throw new PizzaOrderException("Order not found!");
        }
        else {
            PizzaOrder o = order.get();
            o.setQuantity(o.getQuantity() + 1);
            repository.save(o);
        }
    }

Do not forget to call repository.save(o);. Otherwise it is not written back to the database!

    public void addWrongOrder() {
        PizzaOrder order;

        // -20 Euro!!!
        order = new PizzaOrder(LocalDate.now(), "Margherita", "Large", -20.0, 1, true);
        repository.save(order);
    }

This method is useless.

    public void fillTestData(int anz) {
        PizzaOrder p;
        Faker faker;
        String[] PIZZAS = {"Margherita", "Salami", "Tonno", "Hawaii", "Funghi", "Diavolo"};
        String[] SIZES = { "Small", "Medium", "Large", "Family" };

        faker = new Faker();
        //orders.clear();

        for (int i = 0; i < anz; i++) {
            p = new PizzaOrder();
            p.setOrderDate(LocalDate.now().minusDays(faker.number().numberBetween(0, 30)));
            p.setPizza(PIZZAS[faker.number().numberBetween(0, PIZZAS.length)]);
            p.setSize(SIZES[faker.number().numberBetween(0, SIZES.length)]);
            p.setPrice(faker.number().randomDouble(2, 8, 30));
            p.setQuantity(faker.number().numberBetween(1, 6));
            p.setGarlic(faker.bool().bool());

            repository.save(p);
        }
    }

Do not forget to call repository.save(o);.

    @Override
    public String toString() {
        return repository.findAll()
                .stream()
                .map(PizzaOrder::toString)
                .reduce("", (a, b) -> a + b + "\n");
    }

Same.

005.png
006.png
007.png
008.png