spring的事务和mysql的事务_Spring Boot 18 Hibe.Spring管理事务

18.1 创建数据库表

Mysql:-- Create tablecreate table BANK_ACCOUNT(  ID        BIGINT not null,  NAME VARCHAR(128) not null,  BALANCE   DOUBLE not null) ;--  alter table BANK_ACCOUNTadd constraint BANK_ACCOUNT_PK primary key (ID);  Insert into Bank_Account(ID, Name, Balance) values (1, 'zhangsan', 1000);Insert into Bank_Account(ID, Name, Balance) values (2, 'lisi', 2000);Insert into Bank_Account(ID, Name, Balance) values (3, 'wangwu', 3000);
e40ce8fb64a39b31aced40d40c1aed9b.png

18.2 创建Spring Boot项目

b1db838480100b61db5a816518c698fd.png
43886b446bae5980fe6519083226cde1.png

提示:Spring Data JPA中已经包含了Hibernate

18.3 完整的pom.xml

<?xml version="1.0" encoding="UTF-8"?>4.0.0org.springframework.bootspring-boot-starter-parent2.2.11.RELEASEme.laocatSpringBootHibernate1.0.0SpringBootHibernateSpring Boot and Hibernate1.8org.springframework.bootspring-boot-starter-data-jpaorg.springframework.bootspring-boot-starter-thymeleaforg.springframework.bootspring-boot-starter-weborg.threeten            threetenbp            1.3.6mysqlmysql-connector-javaruntimeorg.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-engineorg.springframework.bootspring-boot-maven-plugin

18.4 配置Hibernate

为了使 Spring可以连接到数据库,您需要在application.properties文件中配置必要的参数 。

application.properties

# ===============================# DATABASE# ===============================spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/springboothibernate?serverTimezone=UTCspring.datasource.username=rootspring.datasource.password=root# ===============================# JPA/HIBERNATE# ===============================spring.jpa.show-sql=truespring.jpa.hibernate.ddl-auto=updatespring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialectspring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext

注意:默认情况下, Spring Boot将自动配置 JPA,并创建 与JPA相关的 Spring bean 。Spring Boot的这些自动配置 包括:

  1. 数据源自动配置
  2. DataSourceTransactionManagerAutoConfiguration
  3. HibernateJpaAutoConfiguration

该应用程序的目的是使用 Hibernate,因此,我们需要禁用上述Spring Boot的自动配置

me.laocat.hibernate.SpringBootHibernateApplication

package me.laocat.hibernate;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 }) // Hibernate自动配置public class SpringBootHibernateApplication {public static void main(String[] args) {SpringApplication.run(SpringBootHibernateApplication.class, args);}}

然后,配置Hibernate所需 的 Spring Bean

me.laocat.hibernate.SpringBootHibernateApplication

package me.laocat.hibernate;import java.io.IOException;import java.util.Properties;import javax.sql.DataSource;import org.hibernate.SessionFactory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;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 }) // Hibernate自动配置public class SpringBootHibernateApplication {private static final Logger log = LoggerFactory.getLogger(SpringBootHibernateApplication.class);@Autowiredprivate Environment env;public static void main(String[] args) {SpringApplication.run(SpringBootHibernateApplication.class, args);}// 获取数据源@Bean(name = "dataSource")public DataSource getDataSource() {DriverManagerDataSource dataSource = new DriverManagerDataSource();// 参见: application.propertiesdataSource.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"));log.info("getDataSource:{}", dataSource);return dataSource;}// 获取Hibernate的SessionFactory@Autowired@Bean(name="sessionFactory")public SessionFactory getSessionFactory(DataSource dataSource) throws IOException {Properties hibernateProperties = new Properties();hibernateProperties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));hibernateProperties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));hibernateProperties.put("current_session_context_class",env.getProperty("spring.jpa.properties.hibernate.current_session_context_class"));LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();factoryBean.setPackagesToScan(new String[] {""});factoryBean.setDataSource(dataSource);factoryBean.setHibernateProperties(hibernateProperties);factoryBean.afterPropertiesSet();SessionFactory sessionFactory = factoryBean.getObject();log.info("getSessionFactory:{}",sessionFactory);return sessionFactory;}// 获取Hibernate事务管理器@Autowired@Bean(name="transactionManager")public HibernateTransactionManager getHibernateTransactionManager(SessionFactory sessionFactory) {HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);return transactionManager;}}

18.5 Entity, Model, Form, DAO

JPA(或 Hibernate)中, Entity 是数据库中表的代表类(对应于)。此类的字段将对应于表的列。
我们将创建一个 BankAccount 类来代表 数据库中的 BANK_ACCOUNTJPA批注将用于对字段进行批注,以描述字段和表的列之间的映射。这些映射是1-1。每个字段对应于表的1列。

6f6caff58c3e71dcbde977dbb88bdb49.png

me.laocat.hibernate.entity.BankAccount

package me.laocat.hibernate.entity;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.Table;/** * 描述: 银行账户 * @author 技术猫 */@Entity@Table(name = "Bank_Account")public class BankAccount {@Id    @GeneratedValue    @Column(name = "id", nullable = false)    private Long id;@Column(name = "name", length = 128, nullable = false)private String name;@Column(name = "Balance", nullable = false)    private double balance;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}}

Entity表示表的一条记录的数据,而Model表示查询语句的一条记录的数据(从一个或多个表连接)。当您对一个或多个表的某些列感兴趣时,您可以使用Model类。

Model类:

package me.laocat.hibernate.model;/** * 描述: 银行账户模型类 * @author laocat */public class BankAccountInfo {private Long id;    private String name;    private double balance;    public BankAccountInfo() {}public BankAccountInfo(Long id, String name, double balance) {super();this.id = id;this.name = name;this.balance = balance;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}}

自定义异常类:

package me.laocat.hibernate.exception;/** * 描述:自定义异常类  * @author laocat */public class BankTransactionException extends Exception {private static final long serialVersionUID = 1196803155571800009L;public BankTransactionException(String message) {super(message);}}

BankAccountDAO.java

package me.laocat.hibernate.dao;import java.util.List;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.query.Query;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Repository;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import me.laocat.hibernate.entity.BankAccount;import me.laocat.hibernate.exception.BankTransactionException;import me.laocat.hibernate.model.BankAccountInfo;@Repository@Transactionalpublic class BankAccountDAO {@Autowiredprivate SessionFactory sessionFactory;public BankAccountDAO() {}/** * 根据ID查询账户 * @param id * @return */public BankAccount findById(Long id) {Session session = this.sessionFactory.getCurrentSession();return session.get(BankAccount.class, id);}/** * 查询所有账户 * @return */public List listBankAccountInfo() {String sql = "Select new " + BankAccountInfo.class.getName() //+ "(e.id,e.name,e.balance) " //+ " from " + BankAccount.class.getName() + " e ";Session session = this.sessionFactory.getCurrentSession();Query query = session.createQuery(sql, BankAccountInfo.class);return query.getResultList();}/** * 入账 * @param id 账户ID * @param amount 金额 * @throws BankTransactionException */// propagation: 表示传播特性// MANDATORY: 表示支持当前事务,如果事务不存在将报异常,所以必须先创建事务.    @Transactional(propagation = Propagation.MANDATORY)    public void addAmount(Long id, double amount) throws BankTransactionException {        BankAccount account = this.findById(id);        if (account == null) {            throw new BankTransactionException("账户没找到 " + id);        }        double newBalance = account.getBalance() + amount;        if (account.getBalance() + amount < 0) {            throw new BankTransactionException(                    "账户金额 '" + id + "' 不足 (" + account.getBalance() + ")");        }        account.setBalance(newBalance);    }                /**     * 转账      * @param fromAccountId 转出账户     * @param toAccountId 转入账户     * @param amount 金额     * @throws BankTransactionException     */    // 不要在此方法中捕获 BankTransactionException.    // REQUIRES_NEW: 表示创建一个新事务,如果存在当前事务,则挂起当前事务    @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);    }}

SendMoneyForm.java

解释Spring事务的运行机制:

在这个示例中,我模拟了一个银行事务。A账户寄给B账户700元。因此,将在数据库中创建两个动作:

B账户增加700元

从A账户中减去700元。

如果第一次操作成功(给B账户增加700元),但第二次操作因为某种原因而失败,银行将遭受损失。

因此,它需要管理事务,以确保如果操作失败,数据将回滚原始状态(在事务之前)。当所有操作都成功时,事务被认为是成功的。

0454efed7dfa6c8b7555a713f6f8d49b.png

18.6 Controller

package me.laocat.hibernate.controller;import java.util.List;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;import me.laocat.hibernate.dao.BankAccountDAO;import me.laocat.hibernate.exception.BankTransactionException;import me.laocat.hibernate.form.SendMoneyForm;import me.laocat.hibernate.model.BankAccountInfo;@Controllerpublic class BanAccountController {@Autowired    private BankAccountDAO bankAccountDAO;     @RequestMapping(value = "/", method = RequestMethod.GET)    public String showBankAccounts(Model model) {        List 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:/";    }}

18.7 Thymeleaf 模板

72fdb33d67749443cdd239c130dd2667.png
账户 | 转账

accountsPage.html

银行

所有账户

ID Name Balance .. .. ..

sendMoneyPage.html

         银行

转账

1 - zhangsan 2 - lisi 3 - wangwu
..
转出账户ID 转入账户ID 金额

18.8 启动应用

访问:http://localhost:8080/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值