codestory

Die Anleitung zu Spring Boot, Hibernate und Spring Transaction

  1. Das Zweck des Artikel
  2. Die Database vorbereiten
  3. Das Projekt Spring Boot erstellen
  4.  pom.xml konfigurieren
  5. Hibernate konfigurieren
  6. Entity, Model, Form, DAO
  7. Controller
  8. Thymeleaf Template
  9. Die Applikation laufen

1. Das Zweck des Artikel

Der Artikel wird nach ... geschrieben:
  • Spring Boot 2.x
  • Hibernate 5.x
  • Eclipse 4.7 (Oxygen)
Im Artikel leite ich Sie bei der Erstellung eines Projekt Spring Boot und Umgang mit der Database (Oracle, MySQL, SQL Server, Postgres,..) benutzend Hibernate & Spring Transaction. Die Fragen, die im Artikel diskutiert werden, sind :
  • Die notwendigen Bibliotheke deklarieren um mit der Database zu arbeiten
  • Spring Boot konfigurieren um mit der Database zu verbinden.
  • Mit der Database verwendend Session von JPA manipulieren.
  • Spring Transaction verwenden und den Grundsatz vom Spring Transaction erklären.

2. Die Database vorbereiten

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. Das Projekt Spring Boot erstellen

Auf Eclipse erstellen Sie ein Projekt Spring Boot.
Geben Sie ein:
  • Name: SpringBootHibernate
  • Group: org.o7planning
  • Artifact: SpringBootHibernate
  • Description: Spring Boot + Hibernate + Spring Transaction
  • Package: org.o7planning.sbhibernate
Die Technologie und die Bibliothek zur Verwendung auswählen :
  • JPA
  • MySQL
  • PostgrsSQL
  • SQL Server
  • Web
  • Thymeleaf
Achtung: Wenn Sie JPA wählen, schließt es die Bibliothek für Hibernate ein.

4.  pom.xml konfigurieren

Wenn Sie mit der Database Oracle arbeiten, sollen Sie die folgenden Bibliothek auf pom.xml deklarieren:
* 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>
Wenn Sie mit der Database SQL Service verbinden, können Sie eine der 2 Bibliothek JTDS oder Mssql-Jdbc verwenden:
* 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>
Die volle Inhalt der File 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. Hibernate konfigurieren

Damit Spring in die Database verbinden kann, sollen Sie die notwendigen Parameter in die File application.properties konfigurieren.
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
Achtung: Spring Boot wird nach dem Standard JPA automatisch konfigurieren und die Spring BEAN beziehend mit JPA erstellen. Die automatischen Konfiguration vom Spring Boot fasst um:
  • DataSourceAutoConfiguration
  • DataSourceTransactionManagerAutoConfiguration
  • HibernateJpaAutoConfiguration
Das Zweck in der Applikation ist die Verwendung von Hibernate, Deshalb sollen Sie die obengemeinten automatischen Konfiguration vom Spring Boot deaktivieren.
** 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);
    }
    .......
}
Danach konfigurieren Siw die notwendigen Spring BEAN für 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

Im JPA (oder Hibernate) ist Entity eine Vertretungsklasse (entsprechen) einer Tabelle in der Database. Die Felder (field) in dieser Klasse entprechen den Säule in der Tabelle.
Wir werden die Klasse BankAccount erstellen um die Tabelle BANK_ACCOUNT in die Database zu vertreten. Die JPA Annotation werden verwendet um auf die Felder zur Abbildung zwischen die Felder und die Säule in der Tabelle zu annotieren. Die Abbildung ist 1-1, jeder Feld entspricht einer Säule in der Tabelle
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;
    }
}
Inzwischen vertritt eine Klasse Entity die Database eines Rekord der Tabelle , vertritt eine Klasse Model die Database vom Rekord eines Abfragenstatement ( aus einer oder mehr Tabelle beteiligen (join). Sie verwenden die Klasse Model wenn Sie sich für die Säule einer oder mehrer Tabelle interessieren.
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;
    }
}
Das Operationsmechanismus vom Spring Transaction erklären:
In diesem Beispiel bezeichne ich eine Bank-Transaction. Das Konto A überträgt dem Konto B einen Betrag von 700$. Deshalb gibt es 2 Aktionen, die in Database erstellt werden
  • 700$ ins Konto B einfügen.
  • 700$ aus Konto A subtrahieren.
Wenn die erste Aktion (700$ ins Konto B einfügen) erfolgreich ist, aber die 2.Aktion ist wegen eines Grund nicht erfolgreich. In diesem Fall bekommt die Bank einen Schaden.
Deshalb brauchen wir die Transaktion (Transaction) kontrollieren um zu guarantieren, dass wenn eine Aktion nicht erfolgreich ist, wird die Daten zum Anfangstand (vor der Transaktion) zurückgekehrt. Die Transaktion ist nur erfolgreich fertig wenn alle Aktionen erfolgreich sind
Verwenden Sie @Transactional(rollbackFor = BankTransactionException.class) zur Annotierung auf einer Methode um "Spring Transaction" zu sagen " Wenden Sie AOP für diese Methode an"
@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);
}
Spring Transaction wendet Spring AOP für Ihre Applikation an. Sie ist so gleich wie die Änderung der Kode von der Methode, das Einfügen der Code zur Ausnahmefangen und die Aufruf auf Rollback der Transaktion wenn die Ausnahme auftritt. Danach wirf sie die Ausnahme aus der Methode. Alle sind gleich wie folgend:

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. Die Applikation laufen

Auf Eclipse laufen Sie Ihre Applikation

Anleitungen Spring Boot

Show More