Overslaan naar hoofdinhoud

Working with Data

Openen in ChatGPT

In deze stap leer je hoe je een datamodel kunt maken met Spring en die gegevens visueel kunt weergeven. Aan het einde van deze stap heeft de app die in de vorige stap is gemaakt, Creating a Basic App, een tabel die gegevens over klanten weergeeft. Door het volgen van deze stappen leer je over:

  • Spring annotaties
  • Gegevensbeheer
  • De webforJ Table component

Als je deze stap hebt voltooid, maak je een versie van 2-working-with-data.

De app uitvoeren

Terwijl je je app ontwikkelt, kun je 2-working-with-data als vergelijking gebruiken. Om de app in actie te zien:

  1. Navigeer naar de bovenliggende map die het pom.xml bestand bevat, dit is 2-working-with-data als je de versie op GitHub volgt.

  2. Gebruik het volgende Maven-commando om de Spring Boot-app lokaal uit te voeren:

    mvn

De app opent automatisch een nieuwe browser op http://localhost:8080.

Afhankelijkheden en configuraties

Deze tutorial gebruikt de H2-database en in een toekomstige stap de Jakarta Persistentie-API (JPA) via Spring Data JPA. Dit vereist dat je afhankelijkheden toevoegt aan pom.xml en application.properties bijwerkt. Dit is de laatste keer dat je deze twee bestanden hoeft te wijzigen voor de rest van de tutorial.

Voeg in je POM de volgende afhankelijkheden toe:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

Voeg in application.properties, binnen src/main/resources, het volgende toe:

# H2 Database configuratie
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA configuratie
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
Gegevens toegankelijk maken

Deze tutorial gebruikt een in-memory database en de standaard referenties voor het toegang krijgen tot gegevens. Ga naar de Spring Data Access documentatie om meer te leren over specifieke Spring Boot configuratieopties.

Spring beans

Een belangrijk onderdeel van het gebruik van het Spring-framework is het begrijpen van wat beans zijn. Beans zijn objecten met gedefinieerde Spring-annotaties die het eenvoudiger maken voor Spring om ze te configureren door de bedoelde functie van de klasse te begrijpen. Ga naar Spring’s Bean Overview documentatie om meer te leren.

Een datamodel maken

Voordat je de gegevens visueel kunt weergeven of creëren, heeft deze tutorial een manier nodig om de gegevens van elke klant weer te geven, inclusief hun naam, land en bedrijf. Met Spring wordt dit gedaan met een klasse die een @Entity annotatie heeft.

Maak een klasse in src/main/java/com/webforj/tutorial/entity met de naam Customer.java. Het moet de @Entity annotatie hebben en getter- en setter-methoden bevatten voor de klantwaarden, behalve voor de id. In plaats van een creatiemethode voor id waarden te gebruiken, gebruik je de annotaties @Id en @GeneratedValue om te garanderen dat elke klant een unieke id krijgt.

@Entity
@Table(name = "customers")
public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String firstName = "";
private String lastName = "";
private String company = "";
private Country country = Country.UNKNOWN;

public enum Country {
UNKNOWN,
GERMANY,
ENGLAND,
ITALY,
USA
}

public Customer(String firstName, String lastName, String company, Country country) {
setFirstName(firstName);
setLastName(lastName);
setCompany(company);
setCountry(country);
}

public Customer(String firstName, String lastName, String company) {
this(firstName, lastName, company, Country.UNKNOWN);
}

public Customer(String firstName, String lastName) {
this(firstName, lastName, "");
}

public Customer(String firstName) {
this(firstName, "");
}

public Customer() {
}

public void setFirstName(String newName) {
firstName = newName;
}

public String getFirstName() {
return firstName;
}

public void setLastName(String newName) {
lastName = newName;
}

public String getLastName() {
return lastName;
}

public void setCompany(String newCompany) {
company = newCompany;
}

public String getCompany() {
return company;
}

public void setCountry(Country newCountry) {
country = newCountry;
}

public Country getCountry() {
return country;
}

public Long getId() {
return id;
}

}

Met het Customer datamodel op zijn plaats, kun je nu beginnen met het toevoegen van bedrijfslogica aan je app.

Gegevens beheren

Na het creëren van een datamodel, maak je een repository en een service om de klantgegevens te beheren. Het maken van deze soorten klassen in je app stelt je in staat om operaties uit te voeren, zoals het toevoegen, verwijderen en bijwerken van klantrecords.

Een repository maken

Het maken van een repository maakt de gegevens van de entiteiten toegankelijk, zodat je app meerdere klanten kan bevatten. Het doel van deze tutorial is om de gegevens bewerkbaar, sorteerbaar en valideerbaar te maken. Je bepaalt de mogelijkheden van een repository door de Spring Data repository die je gebruikt.

In een toekomstige stap, Validating and Binding Data, heb je toegang nodig tot Spring Data JPA om klant eigenschappen te valideren. Daarom is de geschikte repository om te gebruiken de JpaRepository.

In src/main/java/com/webforj/tutorial/repository, maak een repository-interface die de Spring @Repository annotatie heeft en JpaRepository uitbreidt. Je moet opgeven welk type entiteiten in deze repository staat en welk type object de id is. Voor de zekerheid breid je ook JpaSpecificationExecutor uit. Deze toevoeging stelt je in staat om later, indien nodig, geavanceerde filteropties te implementeren.

CustomerRepository.java
@Repository
public interface CustomerRepository
extends JpaRepository<Customer, Long>,
JpaSpecificationExecutor<Customer> {
}

De CustomerRepository die je net hebt gemaakt, heeft geen gedeclareerde methoden. De methoden voor het beheren van de gegevens (de bedrijfslogica van de app) zullen zich in een serviceklasse bevinden.

Spring documentatie links

Hier zijn vier links naar de documentatie van Spring die je zullen helpen om Spring repositories beter te begrijpen:

Een service maken

In src/main/java/com/webforj/tutorial/service, maak een CustomerService klasse. Deze service bevat methoden om klanten te creëren, bij te werken, te verwijderen en op te vragen met behulp van CustomerRepository.

Bovendien heeft deze service een mechanisme nodig om Spring Data repositories te koppelen aan de UI-componenten van webforJ. Het gebruik van de SpringDataRepository webforJ klasse laat je deze brug creëren. Het vereenvoudigt gegevensbinding en CRUD-operaties, waardoor je webforJ-tabellen en -formulieren vrij kunnen werken met je door Spring beheerde gegevenslaag. Zie meer informatie over de Spring-integratie van webforJ in het artikel Spring Data JPA.

Voor deze serviceklasse gebruik je twee Spring-annotaties:

  • @Service - Dit markeert een klasse als een servicecomponent in Spring, waardoor deze automatisch wordt gedetecteerd en beheerd als een bean voor bedrijfslogica of herbruikbare operaties.

  • @Transactional - Deze annotatie vertelt Spring om de methode of klasse binnen een database transactie uit te voeren, zodat alle operaties binnen samen worden gecommit of teruggedraaid. Meer details zijn beschikbaar in de documentatie van Spring, Using @Transactional.

CustomerService.java
@Service
@Transactional
public class CustomerService {
private final CustomerRepository repository;

public CustomerService(CustomerRepository repository) {
this.repository = repository;
}

public Customer createCustomer(Customer customer) {
return repository.save(customer);
}

public Customer updateCustomer(Customer customer) {
if (!repository.existsById(customer.getId())) {
throw new IllegalArgumentException("Klant niet gevonden met ID: " + customer.getId());
}
return repository.save(customer);
}

public void deleteCustomer(Long id) {
if (!repository.existsById(id)) {
throw new IllegalArgumentException("Klant niet gevonden met ID: " + id);
}
repository.deleteById(id);
}

public long getTotalCustomersCount() {
return repository.count();
}

public SpringDataRepository<Customer, Long> getRepositoryAdapter() {
return new SpringDataRepository<>(repository);
}

public Customer getCustomerByKey(Long id) {
return repository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Klant niet gevonden met ID: " + id));
}

public boolean doesCustomerExist(Long id) {
return repository.existsById(id);
}

}

Initiële gegevens laden

Voor deze tutorial komt de initiële klanten dataset uit een JSON-bestand. De Java-app laadt het bestand, niet de browser, dus maak het binnen src/main/resources/data met de volgende gegevens:

[
{
"firstName": "Alice",
"lastName": "Smith",
"company": "TechCorp",
"country": "GERMANY"
},
{
"firstName": "John",
"lastName": "Doe",
"company": "Innovatech",
"country": "ITALY"
},
{
"firstName": "Emma",
"lastName": "Brown",
"company": "SoftSolutions",
"country": "ENGLAND"
},
{
"firstName": "Liam",
"lastName": "Jones",
"company": "FinWise",
"country": "UNKNOWN"
},
{
"firstName": "Sophia",
"lastName": "Taylor",
"company": "DataWorks",
"country": "GERMANY"
},
{
"firstName": "Noah",
"lastName": "Wilson",
"company": "EcoBuild",
"country": "ITALY"
},
{
"firstName": "Olivia",
"lastName": "Moore",
"company": "NextGen",
"country": "ENGLAND"
},
{
"firstName": "James",
"lastName": "Anderson",
"company": "BlueTech",
"country": "UNKNOWN"
},
{
"firstName": "Isabella",
"lastName": "Thomas",
"company": "FutureLogic",
"country": "GERMANY"
},
{
"firstName": "Lucas",
"lastName": "White",
"company": "GreenEnergy",
"country": "ITALY"
}
]

Vervolgens heeft de app een manier nodig om deze gegevens op te halen wanneer deze opstart. Maak in src/main/java/com/webforj/tutorial/config een DataInitializer klasse. Nu, wanneer de app draait, als er geen klanten gedetecteerd worden, worden de klanten uit het JSON-bestand geladen en in de H2-database geplaatst:

DataInitializer.java
@Component
public class DataInitializer implements CommandLineRunner {
private final CustomerService customerService;

public DataInitializer(CustomerService customerService) {
this.customerService = customerService;
}

@Override
public void run(String... args) {
if (customerService.getTotalCustomersCount() == 0) {
loadCustomersFromJson();
}
}

private void loadCustomersFromJson() {
ObjectMapper mapper = new ObjectMapper();
try (InputStream is = getClass().getResourceAsStream("/data/customers.json")) {
List<Customer> customers = mapper.readValue(is, new TypeReference<List<Customer>>() {
});
for (Customer customer : customers) {
customerService.createCustomer(customer);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

Gegevens visueel weergeven

Het laatste onderdeel van deze stap is het gebruik van de Table component en het verbinden met de Spring-gegevens.

Een instantie van een webforJ Table heeft een datatype nodig om te werken, dat is de entiteitsklasse die eerder in deze stap is gemaakt:

Table<Customer> table = new Table<>();

Zodra je een Table hebt, krijgt elke klant-eigenschap zijn eigen kolom. Voor elke kolom die je toevoegt, gebruik je de naam van de eigenschap, de getter-methode in de Customer entiteit en de setLabel() methode om de informatie weer te geven in de volgorde die je wilt:

table.addColumn("firstName", Customer::getFirstName).setLabel("Voornaam");
table.addColumn("lastName", Customer::getLastName).setLabel("Achternaam");
table.addColumn("company", Customer::getCompany).setLabel("Bedrijf");
table.addColumn("country", Customer::getCountry).setLabel("Land");

Nadat je de kolommen hebt toegevoegd, moet je specificeren welke repository de Table moet gebruiken om zijn gegevens te vullen. Deze app haalt de repository van de getRepositoryAdapter() methode in de gemaakte CustomerService:

table.setRepository(customerService.getRepositoryAdapter());

Tabelgrootte

Voor de tabel kun je setSize() gebruiken om de grootte in pixels of andere CSS-eenheden in te stellen. Door een maximale breedte in te stellen ten opzichte van de schermbreedte, help je je app om meer adaptief te zijn voor kleinere schermen.

Voor de kolommen kun je de breedtes individueel instellen, of een van de Table methoden zoals setColumnsToAutoFit() gebruiken om webforJ de breedtes voor jou te laten afhandelen:

table.setSize("1000px", "294px");
table.setMaxWidth("90vw");
table.setColumnsToAutoFit();

Gebruikersinteracties

De Table component heeft ook methoden om te controleren hoe gebruikers met de kolommen interageren:

table.setColumnsToResizable(false);
table.getColumns().forEach(column -> column.setSortable(true));

De gemarkeerde delen van de Application klasse voegen de Table component toe, definiëren de kolommen en gebruiken CustomerService om de repository op te halen:

Application.java
@SpringBootApplication
@BundleEntry("css/card.css")
@AppTheme("system")
@AppProfile(name = "Klant Applicatie", shortName = "KlantApp")
public class Application extends App {

//Voeg een constructor-injectie toe voor CustomerService
private final CustomerService customerService;

public Application(CustomerService customerService) {
this.customerService = customerService;
}

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@Override
public void run() throws WebforjException {
Frame mainFrame = new Frame();
Paragraph tutorial = new Paragraph("Tutorial App!");
Button btn = new Button("Info");

//Voeg de Table component toe
Table<Customer> table = new Table<>();

mainFrame.setWidth("fit-content");
mainFrame.addClassName("card");

//Style de Table component, stel de kolommen in en stel de repository in
table.setSize("1000px", "294px");
table.setMaxWidth("90vw");
table.addColumn("firstName", Customer::getFirstName).setLabel("Voornaam");
table.addColumn("lastName", Customer::getLastName).setLabel("Achternaam");
table.addColumn("company", Customer::getCompany).setLabel("Bedrijf");
table.addColumn("country", Customer::getCountry).setLabel("Land");
table.setColumnsToAutoFit();
table.setColumnsToResizable(false);
table.getColumns().forEach(column -> column.setSortable(true));
table.setRepository(customerService.getRepositoryAdapter());

btn.setTheme(ButtonTheme.PRIMARY)
.setMaxWidth(200)
.addClickListener(e -> OptionDialog.showMessageDialog("Dit is een tutorial!", "Info"));

//Voeg de Table toe aan het Frame
mainFrame.add(tutorial, btn, table);
}

}

Volgende stap

Met deze wijzigingen laadt de app klantgegevens in de database en geeft deze weer in een Table component. De volgende stap, Routing and Composites, introduceert routing en meerdere weergaven voor het toevoegen van nieuwe klanten.