devstory

Le Tutoriel de Spring Boot, Hibernate et Spring Transaction

  1. Objectif de la publication
  2. Préparer une base de données
  3. Créer un projet Spring Boot
  4. Configurer le fichier pom.xml
  5. Configurer Hibernate
  6. Entity, Model, Form, DAO
  7. Controller
  8. Thymeleaf Template
  9. Exécuter l'application

1. Objectif de la publication

Ce document est basé sur :
  • Spring Boot 2.x
  • Hibernate 5.x
  • Eclipse 4.7 (Oxygen)
Dans cette publication, je vais vous donner des instructions de créer d'un projet Spring Boot et de travailler avec une base de données (Oracle, MySQL, SQL Server, Postgres,..) à l'aide de Hibernate & Spring Transaction. Les questions discutées dans cet article sont :
  • Déclarer les bibliothèques nécessaires afin d'être capable à travailler avec une base de données.
  • Configurer Spring Boot afin d'être capable de se connecter à une base de données.
  • Manipuler une base de données à l'aide de Session de Hibernate.
  • Utiliser Spring Transaction et expliquer la principe d'opération de Spring Transaction.

2. Préparer une base de données

MySQL / SQL Server
-- Create table
create table BANK_ACCOUNT
(
  ID        BIGINT not null,
  FULL_NAME VARCHAR(128) not null,
  BALANCE   DOUBLE not null
) ;
--  
alter table BANK_ACCOUNT
  add constraint BANK_ACCOUNT_PK primary key (ID);


Insert into Bank_Account(ID, Full_Name, Balance) values (1, 'Tom', 1000);
Insert into Bank_Account(ID, Full_Name, Balance) values (2, 'Jerry', 2000);
Insert into Bank_Account(ID, Full_Name, Balance) values (3, 'Donald', 3000);

commit;
SQL Server
-- Create table
create table BANK_ACCOUNT
(
  ID        BIGINT not null,
  FULL_NAME VARCHAR(128) not null,
  BALANCE   DOUBLE PRECISION not null
) ;
--  
alter table BANK_ACCOUNT
  add constraint BANK_ACCOUNT_PK primary key (ID);


Insert into Bank_Account(ID, Full_Name, Balance) values (1, 'Tom', 1000);
Insert into Bank_Account(ID, Full_Name, Balance) values (2, 'Jerry', 2000);
Insert into Bank_Account(ID, Full_Name, Balance) values (3, 'Donald', 3000);
Oracle
-- Create table
create table BANK_ACCOUNT
(
  ID        NUMBER(19) not null,
  FULL_NAME VARCHAR2(128) not null,
  BALANCE   NUMBER not null
) ;
--  
alter table BANK_ACCOUNT
  add constraint BANK_ACCOUNT_PK primary key (ID);


Insert into Bank_Account(ID, Full_Name, Balance) values (1, 'Tom', 1000);
Insert into Bank_Account(ID, Full_Name, Balance) values (2, 'Jerry', 2000);
Insert into Bank_Account(ID, Full_Name, Balance) values (3, 'Donald', 3000);

commit;
PostGres
Create table Bank_Account (
   ID Bigint not null,
   Full_Name Varchar(128) not null,
   Balance real not null,
   CONSTRAINT Bank_Account_pk PRIMARY KEY (ID)
);

Insert into Bank_Account(ID, Full_Name, Balance) values (1, 'Tom', 1000);
Insert into Bank_Account(ID, Full_Name, Balance) values (2, 'Jerry', 2000);
Insert into Bank_Account(ID, Full_Name, Balance) values (3, 'Donald', 3000);

3. Créer un projet Spring Boot

Sur Eclipse, créez un projet Spring Boot.
Saisissez :
  • Name: SpringBootHibernate
  • Group: org.o7planning
  • Artifact: SpringBootHibernate
  • Description: Spring Boot + Hibernate + Spring Transaction
  • Package: org.o7planning.sbhibernate
Sélectionnez les technologies et les bibliothèques qui seront utilisées :
  • JPA
  • MySQL
  • PostgrsSQL
  • SQL Server
  • Web
  • Thymeleaf
REMARQUE: Lorsque vous sélectionnez JPA, il comprendra des bibliothèques de Hibernate.

4. Configurer le fichier pom.xml

Si vous travaillez avec la base de données Oracle, vous devez déclarer les bibliothèques comme ci-dessous sur pom.xml:
* Oracle *
<dependencies>
    .....

     <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc6</artifactId>
        <version>11.2.0.3</version>
    </dependency>
    
    .....
</dependencies>

<repositories>
        ....

    <!-- Repository for ORACLE JDBC Driver -->
    <repository>
        <id>codelds</id>
        <url>https://code.lds.org/nexus/content/groups/main-repo</url>
    </repository>
    
    .....
</repositories>
Si vous vous connectez à la base de données SQL Service, vous pouvez utiliser une des deux bibliothèques JTDS ou Mssql-Jdbc:
* SQL Server *
<dependencies>
       .....

    <dependency>
        <groupId>com.microsoft.sqlserver</groupId>
        <artifactId>mssql-jdbc</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <dependency>
        <groupId>net.sourceforge.jtds</groupId>
        <artifactId>jtds</artifactId>
        <scope>runtime</scope>
    </dependency>

     .....
</dependencies>
Le contenu complet du fichier pom.xml:
pom.xml
<?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>

    <groupId>org.o7planning</groupId>
    <artifactId>SpringBootHibernate</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>SpringBootHibernate</name>
    <description>Spring Boot + Hibernate + Spring Transaction</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <!-- SQL Server - Mssql-Jdbc driver -->
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <!-- SQL Server - JTDS driver -->
        <dependency>
            <groupId>net.sourceforge.jtds</groupId>
            <artifactId>jtds</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.3</version>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/org.threeten/threetenbp -->
        <dependency>
            <groupId>org.threeten</groupId>
            <artifactId>threetenbp</artifactId>
            <version>1.3.6</version>
        </dependency>
        
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <repositories>

        <!-- Repository for ORACLE JDBC Driver -->
        <repository>
            <id>codelds</id>
            <url>https://code.lds.org/nexus/content/groups/main-repo</url>
        </repository>
        
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

5. Configurer Hibernate

Pour que Spring puisse se connecter à la base de données, vous devriez configurer des paramètres nécessaires dans le fichier application.properties.
application.properties (MySQL)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
application.properites (Sql Server + Mssql-Jdbc)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.url=jdbc:sqlserver://tran-vmware-pc\\SQLEXPRESS:1433;databaseName=bank
spring.datasource.username=sa
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
application.properites (Sql Server + JTDS)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=net.sourceforge.jtds.jdbc.Driver
spring.datasource.url=jdbc:jtds:sqlserver://tran-vmware-pc:1433/bank;instance=SQLEXPRESS
spring.datasource.username=sa
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
application.properties (Oracle)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@tran-vmware-pc:1521:db12c
spring.datasource.username=bank
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
application.properties (PostGres)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://tran-vmware-pc:5432/bank
spring.datasource.username=postgres
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL82Dialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext

# Fix Postgres JPA Error:
# Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented.
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
Remarque : Spring Boot va configurer automatiquement JPA par défaut, et créer des Spring BEAN relatifs à JPA. ces cofiguration de Spring Boot contiennent :
  • DataSourceAutoConfiguration
  • DataSourceTransactionManagerAutoConfiguration
  • HibernateJpaAutoConfiguration
Le but de cette application est d'utiliser Hibernate, par conséquent, nous devons désactiver ces configurations automatiques de Spring Boot.
** SpringBootHibernateApplication **
package org.o7planning.sbhibernate;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;

@SpringBootApplication
@EnableAutoConfiguration(exclude = { //
        DataSourceAutoConfiguration.class, //
        DataSourceTransactionManagerAutoConfiguration.class, //
        HibernateJpaAutoConfiguration.class })
public class SpringBootHibernateApplication { 
    public static void main(String[] args) {
        SpringApplication.run(SpringBootHibernateApplication.class, args);
    }
    .......
}
Puis, configurez les Spring BEAN requis pour Hibernate.
SpringBootHibernateApplication.java
package org.o7planning.sbhibernate;

import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;

@SpringBootApplication
@EnableAutoConfiguration(exclude = { //
        DataSourceAutoConfiguration.class, //
        DataSourceTransactionManagerAutoConfiguration.class, //
        HibernateJpaAutoConfiguration.class })

public class SpringBootHibernateApplication {

    @Autowired
    private Environment env;

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

    @Bean(name = "dataSource")
    public DataSource getDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();

        // See: application.properties
        dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));
        System.out.println("## getDataSource: " + dataSource);
        return dataSource;
    }

    @Autowired
    @Bean(name = "sessionFactory")
    public SessionFactory getSessionFactory(DataSource dataSource) throws Exception {
        Properties properties = new Properties();
        // See: application.properties
        properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));
        properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));
        properties.put("current_session_context_class", //
                env.getProperty("spring.jpa.properties.hibernate.current_session_context_class")); 
        // Fix Postgres JPA Error:
        // Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented.
        // properties.put("hibernate.temp.use_jdbc_metadata_defaults",false);
        LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();  
        // Package contain entity classes
        factoryBean.setPackagesToScan(new String[] { "" });
        factoryBean.setDataSource(dataSource);
        factoryBean.setHibernateProperties(properties);
        factoryBean.afterPropertiesSet();
        //
        SessionFactory sf = factoryBean.getObject();
        System.out.println("## getSessionFactory: " + sf);
        return sf;
    }

    @Autowired
    @Bean(name = "transactionManager")
    public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
        return transactionManager;
    }
}

6. Entity, Model, Form, DAO

Dans JPA (Ou Hibernate), Entity est une classe représentative (correspondant à) un tableau d'une base de données. Les champs (field) de cette classe répondra aux colonnes du tableau.
Nous allons créer une classe BankAccount qui représente le tableau BANK_ACCOUNT dans la base de données. Les JPA Annotation seront utilisées pour annoter les champs (field) dans le but de décrire la modélisation (mapping) entre des champs et les colonnes de tableau. Ces reflet est 1-1, chaque champ correspond à une colonne dans le tableau.
BankAccount.java
package org.o7planning.sbhibernate.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "Bank_Account")
public class BankAccount {
    @Id
    @GeneratedValue
    @Column(name = "id", nullable = false)
    private Long id;
    @Column(name = "Full_Name", length = 128, nullable = false)
    private String fullName;
    @Column(name = "Balance", nullable = false)
    private double balance;

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getFullName() {
        return fullName;
    }
    public void setFullName(String fullName) {
        this.fullName = fullName;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
}
Alors qu'une classe Entity représente aux données d'un enregistrement (record) d'un tableau, une classe Model représente aux données d'un enregistrement d'une instruction de requête (jointes à partir d'une ou de plusieurs tableaux). Vous utilisez la classe Model si vous vous intéressez à certaines colonnes d'une ou de plusieurs tableaux.
BankAccountInfo.java
package org.o7planning.sbhibernate.model;

public class BankAccountInfo {
    private Long id;
    private String fullName;
    private double balance;

    public BankAccountInfo() {
    }
    // Used in Hibernate query.
    public BankAccountInfo(Long id, String fullName, double balance) {
        this.id = id;
        this.fullName = fullName;
        this.balance = balance;
    } 
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getFullName() {
        return fullName;
    }
    public void setFullName(String fullName) {
        this.fullName = fullName;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
}
BankAccountDAO.java
package org.o7planning.sbhibernate.dao;

import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.o7planning.sbhibernate.entity.BankAccount;
import org.o7planning.sbhibernate.exception.BankTransactionException;
import org.o7planning.sbhibernate.model.BankAccountInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional
public class BankAccountDAO {
   @Autowired
   private SessionFactory sessionFactory;

   public BankAccountDAO() {
   }
   public BankAccount findById(Long id) {
      Session session = this.sessionFactory.getCurrentSession();
      return session.get(BankAccount.class, id);
   }
   public List<BankAccountInfo> listBankAccountInfo() {
      String sql = "Select new " + BankAccountInfo.class.getName() //
            + "(e.id,e.fullName,e.balance) " //
            + " from " + BankAccount.class.getName() + " e ";
      Session session = this.sessionFactory.getCurrentSession();
      Query<BankAccountInfo> query = session.createQuery(sql, BankAccountInfo.class);
      return query.getResultList();
   }

   // MANDATORY: Transaction must be created before.
   @Transactional(propagation = Propagation.MANDATORY)
   public void addAmount(Long id, double amount) throws BankTransactionException {
      BankAccount account = this.findById(id);
      if (account == null) {
         throw new BankTransactionException("Account not found " + id);
      }
      double newBalance = account.getBalance() + amount;
      if (account.getBalance() + amount < 0) {
         throw new BankTransactionException(
               "The money in the account '" + id + "' is not enough (" + account.getBalance() + ")");
      }
      account.setBalance(newBalance);
   }

   // Do not catch BankTransactionException in this method.
   @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = BankTransactionException.class)
   public void sendMoney(Long fromAccountId, Long toAccountId, double amount) throws BankTransactionException {
      addAmount(toAccountId, amount);
      addAmount(fromAccountId, -amount);
   }
}
BankTransactionException.java
package org.o7planning.sbhibernate.exception;

public class BankTransactionException extends Exception {
    private static final long serialVersionUID = -3128681006635769411L;
    
    public BankTransactionException(String message) {
        super(message);
    }
}
SendMoneyForm.java
package org.o7planning.sbhibernate.form;

public class SendMoneyForm {
    private Long fromAccountId;
    private Long toAccountId;
    private Double amount;

    public SendMoneyForm() {
    }
    public SendMoneyForm(Long fromAccountId, Long toAccountId, Double amount) {
        this.fromAccountId = fromAccountId;
        this.toAccountId = toAccountId;
        this.amount = amount;
    }

    public Long getFromAccountId() {
        return fromAccountId;
    }
    public void setFromAccountId(Long fromAccountId) {
        this.fromAccountId = fromAccountId;
    }
    public Long getToAccountId() {
        return toAccountId;
    }
    public void setToAccountId(Long toAccountId) {
        this.toAccountId = toAccountId;
    }
    public Double getAmount() {
        return amount;
    }
    public void setAmount(Double amount) {
        this.amount = amount;
    }
}
Expliquer du mécanisme d'opération de Spring Transaction:
Dans cet exemple, je stimule une transaction bancaire. Un compte A envoie à un compte B une somme de 700$. Donc, ces deux actions seront créées dans la base de données:
  • Ajoutez 700$ au compte B.
  • Soumettez 700$ du compte A.
Si la première action réussit (l'ajout de 700 $ au compte B), mais que la deuxième action échoue pour certaine raison, la banque subira d'un dommage.
Par conséquent, il doit gérer la transaction (Transaction) pour s'assurer que si une action échoue, les données restaurent l'état d'origine (avant la transaction). La transaction est considérée comme réussie lorsque toutes les actions sont réussies.
Utilisez @Transactional(rollbackFor = BankTransactionException.class) à annoter (annotate) sur une méthode afin de dire à "Spring Transaction" qu' "Appliquons l'AOP à cette méthode".
@Transactional(propagation = Propagation.REQUIRES_NEW,
                         rollbackFor = BankTransactionException.class)
public void sendMoney(Long fromAccountId, Long toAccountId,
                       double amount) throws BankTransactionException {
    addAmount(toAccountId, amount);
    addAmount(fromAccountId, -amount);
}
La Spring Transaction applique Spring AOP à votre méthode, ce qui est comme le changement le code de la méthode, l'ajout l'extrait du code pour attraper les exception et l'appel Rollback lorsque l'exception se produit, puis relance (rethrow) l'exception hors de la méthode. Tous sont les mêmes que l'illustration ci-dessous :

7. Controller

MainController.java
package org.o7planning.sbhibernate.controller;

import java.util.List;
import org.o7planning.sbhibernate.dao.BankAccountDAO;
import org.o7planning.sbhibernate.exception.BankTransactionException;
import org.o7planning.sbhibernate.form.SendMoneyForm;
import org.o7planning.sbhibernate.model.BankAccountInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MainController {
    @Autowired
    private BankAccountDAO bankAccountDAO;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String showBankAccounts(Model model) {
        List<BankAccountInfo> list = bankAccountDAO.listBankAccountInfo();
        model.addAttribute("accountInfos", list);
        return "accountsPage";
    }

    @RequestMapping(value = "/sendMoney", method = RequestMethod.GET)
    public String viewSendMoneyPage(Model model) {
        SendMoneyForm form = new SendMoneyForm(1L, 2L, 700d);
        model.addAttribute("sendMoneyForm", form);
        return "sendMoneyPage";
    }

    @RequestMapping(value = "/sendMoney", method = RequestMethod.POST)
    public String processSendMoney(Model model, SendMoneyForm sendMoneyForm) {
        System.out.println("Send Money::" + sendMoneyForm.getAmount()); 
        try {
            bankAccountDAO.sendMoney(sendMoneyForm.getFromAccountId(), //
                    sendMoneyForm.getToAccountId(), //
                    sendMoneyForm.getAmount());
        } catch (BankTransactionException e) {
            model.addAttribute("errorMessage", "Error: " + e.getMessage());
            return "/sendMoneyPage";
        }
        return "redirect:/";
    }
}

8. Thymeleaf Template

_menu.html
<div xmlns:th="http://www.thymeleaf.org" style="border: 1px solid #ccc;padding:5px;margin-bottom:20px;">

  <a th:href="@{/}">Accounts</a>
     | &nbsp;
   <a th:href="@{/sendMoney}">Send Money</a> 
</div>
accountsPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Bank</title>

<style>
th, td {
    padding: 5px;
}
</style>
</head>
<body>
    <!-- Include _menu.html -->
    <th:block th:include="/_menu"></th:block>
    <h2>Accounts</h2>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>Full Name</th>
            <th>Balance</th>
        </tr>
        <tr th:each="accountInfo : ${accountInfos}">
            <td th:utext="${accountInfo.id}">..</td>
            <td th:utext="${accountInfo.fullName}">..</td>
            <td th:utext="${accountInfo.balance}">..</td>
        </tr>
    </table>
</body>
</html>
sendMoneyPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
   <head>
      <title>Bank</title>
   </head>
   <body>      
      <!-- Include _menu.html -->
      <th:block th:include="/_menu"></th:block>
      <h2>Send Money</h2>
      <ul>
         <li>1 - Tom</li>
         <li>2 - Jerry</li>
         <li>3 - Donald</li>
      </ul>
      <div th:if="${errorMessage!=null}"
           style="color:red;font-style:italic" th:utext="${errorMessage}">..</div>
      <form th:action="@{/sendMoney}" th:object="${sendMoneyForm}" method="POST">
         <table>
           <tr>
              <td>From Bank Account Id</td>
              <td><input type="text" th:field="*{fromAccountId}"/></td>
           </tr>
           <tr>
              <td>To Bank Account Id</td>
              <td><input type="text" th:field="*{toAccountId}"/></td>
           </tr>
            <tr>
              <td>Amount</td>
              <td><input type="text" th:field="*{amount}" /></td>
           </tr>          
           <tr>
              <td>&nbsp;</td>
              <td><input type="submit" value="Send"/></td>
           </tr>      
         </table>      
      </form>
   </body>
</html>

9. Exécuter l'application

Sur Eclipse, exécutez votre application.

Tutoriels Spring Boot

Show More