Руководство Spring Boot, Hibernate и Spring Transaction
1. Цель статьи
Статья основана на:
- Spring Boot 2.x
- Hibernate 5.x
- Eclipse 4.7 (Oxygen)
В данной статье я покажу вам, как создать проект Spring Boot и работать с базой данных (Oracle, MySQL, SQL Server, Postgres,..) используя Hibernate & Spring Transaction. Вопросы, которые будут обсуждены в данной статье включают:
- Объявить необходимые библиотеки для работы с базой данных.
- Конфигурировать Spring Boot, чтобы подключиться к базе данных.
- Манипулировать с базой данных используя Session у Hibernate.
- Использовать Spring Transaction и объяснить принцип работы Spring Transaction.
2. Приготовить базу данных
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. Создать проект Spring Boot
На Eclipse создать проект Spring Boot.
Ввести:
- Name: SpringBootHibernate
- Group: org.o7planning
- Artifact: SpringBootHibernate
- Description: Spring Boot + Hibernate + Spring Transaction
- Package: org.o7planning.sbhibernate
Выбрать технологии и библиотеки, которые будут использоваться:
- JPA
- MySQL
- PostgrsSQL
- SQL Server
- Web
- Thymeleaf
Примечание: Когда вы выбираете JPA, он будет включать библиотеку для Hibernate.
4. Конфигурация pom.xml
Если вы работаете с базой данных Oracle, вам нужно объявить следующие библиотеки на 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>
Если вы подключаетесь к базе данных SQL Service, вы можете использовать одну из 2 библиотек JTDS или 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>
Полное содержание файла 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
Чтобы Spring мог подключиться к базе данных вам нужно конфигурировать необходимые параметры в файле 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
Смотрите также:
Примечание: Spring Boot по умолчанию автоматически конфигурирует JPA, и создает Spring BEAN связанные с JPA, эти автоматические конфигурации у Spring Boot включают:
- DataSourceAutoConfiguration
- DataSourceTransactionManagerAutoConfiguration
- HibernateJpaAutoConfiguration
Цель в данном приложении использовать Hibernate, поэтому нам нужно отключить автоматические конфигурации упомянутые выше у 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);
}
.......
}
Потом конфигурировать необходимые Spring BEAN для 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
В JPA (Или Hibernate), Entity является представительным классом (соответствующим) таблице в базе данных. Поля (field) в данном классе будут соответствовать столбцам в таблице.
Мы создадим класс BankAccount, чтобы представить таблицу BANK_ACCOUNT в базе данных. JPA Annotation будут использованы для аннотаций на полях (field), чтобы описать сопоставление (mapping) между полями и столбцами таблицы. Эти сопоставления являются 1-1, каждое поле соответствует одному столбцу в таблице.
Мы создадим класс BankAccount, чтобы представить таблицу BANK_ACCOUNT в базе данных. JPA Annotation будут использованы для аннотаций на полях (field), чтобы описать сопоставление (mapping) между полями и столбцами таблицы. Эти сопоставления являются 1-1, каждое поле соответствует одному столбцу в таблице.
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;
}
}
В то время как класс Entity представляет данные одной записи (record) таблицы, то класс Model представляет данные одной записи запроса (Join из одной или более таблиц). Вы используете класс Model, когда вас интересуют несколько столбцов одной или более таблиц.
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;
}
}
Объяснение операционного механизма Spring Transaction:
В данном примере я симулирую транзакцию банка. Аккаунт А отправляет аккаунту В сумму 700$. Таким образом будут созданы 2 действия в базе данных:
- Прибавление 700$ в аккаунт B.
- Вычитание 700$ из аккаунта A.
Если первое действие успешно (Прибавление 700$ в аккаунт B), но второе действие неуспешно из-за определенной причины. В данном случае банк будет в убытке.
Поэтому нужно контролировать транзакции (Transaction), чтобы гарантировать если будет неуспешное дейтсвие, данные будут возвращены в начальную стадию (Перед транзакцией). Транзакция считается успешной если все действия успешны.
Поэтому нужно контролировать транзакции (Transaction), чтобы гарантировать если будет неуспешное дейтсвие, данные будут возвращены в начальную стадию (Перед транзакцией). Транзакция считается успешной если все действия успешны.
Используйте @Transactional(rollbackFor = BankTransactionException.class) для аннотации (annotate) на методе, чтобы сказать "Spring Transaction" применить AOP для данного метода.
@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 применяет Spring AOP для вашего метода, он похож на действие изменения кода метода, добавляя в код исключение и вызывает Rollback транзакции когда случается исключение, потом перевыбрасывает (rethrow) исключение за методом. Все похоже на изображение ниже:
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>
|
<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> </td>
<td><input type="submit" value="Send"/></td>
</tr>
</table>
</form>
</body>
</html>
Руководства Spring Boot
- Установите Spring Tool Suite для Eclipse
- Руководство Spring для начинающих
- Руководство Spring Boot для начинающих
- Общие свойства Spring Boot
- Руководство Spring Boot и Thymeleaf
- Руководство Spring Boot и FreeMarker
- Руководство Spring Boot и Groovy
- Руководство Spring Boot и Mustache
- Руководство Spring Boot и JSP
- Руководство Spring Boot, Apache Tiles, JSP
- Используйте Logging в Spring Boot
- Мониторинг приложений с помощью Spring Boot Actuator
- Создание веб-приложения с несколькими языками с помощью Spring Boot
- Используйте несколько ViewResolver в Spring Boot
- Используйте Twitter Bootstrap в Spring Boot
- Руководство Spring Boot Interceptor
- Руководство Spring Boot, Spring JDBC и Spring Transaction
- Руководство Spring JDBC
- Руководство Spring Boot, JPA и Spring Transaction
- Руководство Spring Boot и Spring Data JPA
- Руководство Spring Boot, Hibernate и Spring Transaction
- Интеграция Spring Boot, JPA и H2 Database
- Руководство Spring Boot и MongoDB
- Используйте несколько DataSources с Spring Boot и JPA
- Используйте несколько DataSource с Spring Boot и RoutingDataSource
- Создайте приложение для входа с Spring Boot, Spring Security, Spring JDBC
- Создайте приложение для входа с Spring Boot, Spring Security, JPA
- Создайте приложение регистрации пользователей с помощью Spring Boot, Spring Form Validation
- Пример OAuth2 Social Login в Spring Boot.
- Запускать фоновые запланированные задачи в Spring
- Пример CRUD Restful Web Service c Spring Boot
- Пример Spring Boot Restful Client c RestTemplate
- Пример CRUD с Spring Boot, REST и AngularJS
- Защита Spring Boot RESTful Service используя Basic Authentication
- Защита Spring Boot RESTful Service используя Auth0 JWT
- Пример Upload file c Spring Boot
- Пример Download file c Spring Boot
- Пример Upload file c Spring Boot и jQuery Ajax
- Пример Upload file c Spring Boot и AngularJS
- Создание веб-приложения для корзины покупок с помощью Spring Boot, Hibernate
- Руководство Spring Email
- Создайте простое приложение Chat с Spring Boot и Websocket
- Разверните приложение Spring Boot на Tomcat Server
- Развертывание приложения Spring Boot на Oracle WebLogic Server
- Установите бесплатный сертификат Let's Encrypt SSL для Spring Boot
- Настройте Spring Boot для перенаправления HTTP на HTTPS
Show More