Spring-JDBCTemplate & 聲明式事務

 

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

Spring的JdbcTemplate

JdbcTemplate是什麼?

JdbcTemplate是spring框架中提供的一個模板對象,是對原始繁瑣的Jdbc API對象的簡單封裝。java

核心對象

JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);

核心方法

int update(); 執行增、刪、改語句

List<T> query(); 查詢多個
T queryForObject(); 查詢一個
    new BeanPropertyRowMapper<>(); 實現ORM映射封裝

舉個栗子

查詢數據庫全部帳戶信息到Account實體中mysql

public class JdbcTemplateTest {

    @Test
    public void testFindAll() throws Exception {
        // 建立核心對象
        JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());
        // 編寫sql
        String sql = "select * from account";
        // 執行sql
        List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Account.class));
    }

}

Spring整合JdbcTemplate

  • 需求

基於Spring的xml配置實現帳戶的CRUD案例web

  • 步驟分析
1. 建立java項目,導入座標
2. 編寫Account實體類
3. 編寫AccountDao接口和實現類
4. 編寫AccountService接口和實現類
5. 編寫spring核心配置文件
6. 編寫測試代碼

1)建立java項目,導入座標

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.15</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.13</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
</dependencies>

2)編寫Account實體類

public class Account {

    private Integer id;
    private String name;
    private Double money;
}

3)編寫AccountDao接口和實現類

public interface AccountDao {

    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);

}
@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public List<Account> findAll() {
        // 編寫sql
        String sql = "select * from account";
        // 執行sql
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Account.class));
    }

    @Override
    public Account findById(Integer id) {
        // 編寫sql
        String sql = "select * from account where id = ?";
        // 執行sql
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class),id);
    }

    @Override
    public void save(Account account) {
        // 編寫sql
        String sql = "insert into account values(null,?,?)";
        // 執行sql
        jdbcTemplate.update(sql, account.getName(), account.getMoney());
    }

    @Override
    public void update(Account account) {
        // 編寫sql
        String sql = "update account set name = ?,money = ? where id = ?";
        // 執行sql
        jdbcTemplate.update(sql, account.getName(),
        account.getMoney(),account.getId());
    }

    @Override
    public void delete(Integer id) {
        // 編寫sql
        String sql = "delete from account where id = ?";
        // 執行sql
        jdbcTemplate.update(sql, id);
    }
}

4)編寫AccountService接口和實現類

public interface AccountService {

    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);

}
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }

    @Override
    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    @Override
    public void save(Account account) {
        accountDao.save(account);
    }

    @Override
    public void update(Account account) {
        accountDao.update(account);
    }

    @Override
    public void delete(Integer id) {
        accountDao.delete(id);
    }
}

5)編寫spring核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.lagou"/>

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean  >
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <bean  >
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>
</beans>

6)編寫測試代碼

@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    //測試保存
    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("lucy");
        account.setMoney(100d);
        accountService.save(account);
    }

    //測試查詢
    @Test
    public void testFindById() {
        Account account = accountService.findById(3);
        System.out.println(account);
    }

    //測試查詢全部
    @Test
    public void testFindAll() {
        List<Account> accountList = accountService.findAll();
        for (Account account : accountList) {
            System.out.println(account);
        }
    }

    //測試修改
    @Test
    public void testUpdate() {
        Account account = new Account();
        account.setId(3);
        account.setName("rose");
        account.setMoney(2000d);
        accountService.update(account);
    }

    //測試刪除
    @Test
    public void testDelete() {
        accountService.delete(3);
    }
}

實現轉帳案例

  • 步驟分析
1. 建立java項目,導入座標
2. 編寫Account實體類
3. 編寫AccountDao接口和實現類
4. 編寫AccountService接口和實現類
5. 編寫spring核心配置文件
6. 編寫測試代碼

1)建立java項目,導入座標

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.15</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.13</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
</dependencies>

2)編寫Account實體類

public class Account {

    private Integer id;
    private String name;
    private Double money;
    // setter getter....
}

3)編寫AccountDao接口和實現類

public interface AccountDao {

    public void out(String outUser, Double money);

    public void in(String inUser, Double money);

}
@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void out(String outUser, Double money) {
        jdbcTemplate.update("update account set money = money - ? where name = ?", money, outUser);
    }

    @Override
    public void in(String inUser, Double money) {
        jdbcTemplate.update("update account set money = money + ? where name = ?", money, inUser);
    }
}

4)編寫AccountService接口和實現類

public interface AccountService {

    public void transfer(String outUser, String inUser, Double money);
}
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public void transfer(String outUser, String inUser, Double money) {
        accountDao.out(outUser, money);
        accountDao.in(inUser, money);
    }
}

5)編寫spring核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--IOC註解掃描-->
    <context:component-scan base-package="com.lagou"/>

    <!--加載jdbc配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--把數據庫鏈接池交給IOC容器-->
    <bean  >
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!--把JdbcTemplate交給IOC容器-->
    <bean  >
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

</beans>

6)編寫測試代碼

@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() throws Exception {
        accountService.transfer("tom", "jerry", 100d);
    }

}
Spring的事務

Spring中的事務控制方式

Spring的事務控制能夠分爲編程式事務控制和聲明式事務控制。spring

  • 編程式

開發者直接把事務的代碼和業務代碼耦合到一塊兒,在實際開發中不用。sql

  • 聲明式

開發者採用配置的方式來實現的事務控制,業務代碼與事務代碼實現解耦合,使用的AOP思想。數據庫

編程式事務控制相關對象

PlatformTransactionManager

PlatformTransactionManager接口,是spring的事務管理器,裏面提供了咱們經常使用的操做事務的方法。編程

方法 說明
TransactionStatus getTransaction(TransactionDefinition definition); 獲取事務的狀態信息
void commit(TransactionStatus status); 提交事務
void rollback(TransactionStatus status); 回滾事務
  • 注意:
* PlatformTransactionManager 是接口類型,不一樣的 Dao 層技術則有不一樣的實現類。

* Dao層技術是jdbcTemplate或mybatis時:
        DataSourceTransactionManager

* Dao層技術是hibernate時:
        HibernateTransactionManager

* Dao層技術是JPA時:
        JpaTransactionManager

TransactionDefinition

TransactionDefinition接口提供事務的定義信息(事務隔離級別、事務傳播行爲等等)mybatis

方法 說明
int getIsolationLevel() 得到事務的隔離級別
int getPropogationBehavior() 得到事務的傳播行爲
int getTimeout() 得到超時時間
boolean isReadOnly() 是否只讀
  • 1)事務隔離級別

設置隔離級別,能夠解決事務併發產生的問題,如髒讀、不可重複讀和虛讀(幻讀)。併發

* ISOLATION_DEFAULT 使用數據庫默認級別

* ISOLATION_READ_UNCOMMITTED 讀未提交

* ISOLATION_READ_COMMITTED 讀已提交

* ISOLATION_REPEATABLE_READ 可重複讀

* ISOLATION_SERIALIZABLE 串行化
  • 2)事務傳播行爲

事務傳播行爲指的就是當一個業務方法【被】另外一個業務方法調用時,應該如何進行事務控制。app

參數 說明
REQUIRED 若是當前沒有事務,就新建一個事務,若是已經存在一個事務中,加入到這個事務中。通常的選擇(默認值)
SUPPORTS 支持當前事務,若是當前沒有事務,就以非事務方式執行(沒有事務)
MANDATORY 使用當前的事務,若是當前沒有事務,就拋出異常
REQUERS_NEW 新建事務,若是當前在事務中,把當前事務掛起
NOT_SUPPORTED 以非事務方式執行操做,若是當前存在事務,就把當前事務掛起
NEVER 以非事務方式運行,若是當前存在事務,拋出異常
NESTED 若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行REQUIRED 相似的操做
* read-only(是否只讀):建議查詢時設置爲只讀

* timeout(超時時間):默認值是-1,沒有超時限制。若是有,以秒爲單位進行設置

TransactionStatus

TransactionStatus 接口提供的是事務具體的運行狀態。

方法 說明
boolean isNewTransaction() 是不是新事務
boolean hasSavepoint() 是不是回滾點
boolean isRollbackOnly() 事務是否回滾
boolean isCompleted() 事務是否完成

能夠簡單的理解三者的關係:事務管理器經過讀取事務定義參數進行事務管理,而後會產生一系列的事務狀態

實現代碼

1)配置文件

<!--事務管理器交給IOC-->
<bean 
>
    <property name="dataSource" ref="dataSource"/>
</bean>

2)業務層代碼

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Override
    public void transfer(String outUser, String inUser, Double money) {
        // 建立事務定義對象
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 設置是否只讀,false支持事務
        def.setReadOnly(false);
        // 設置事務隔離級別,可重複讀mysql默認級別
        def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
        // 設置事務傳播行爲,必須有事務
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 配置事務管理器
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            // 轉帳
            accountDao.out(outUser, money);
            accountDao.in(inUser, money);

            // 提交事務
            transactionManager.commit(status);
        } catch (Exception e) {
            e.printStackTrace();
            // 回滾事務
            transactionManager.rollback(status);
        }
    }
}

知識小結

Spring中的事務控制主要就是經過這三個API實現的

* PlatformTransactionManager 負責事務的管理,它是個接口,其子類負責具體工做

* TransactionDefinition 定義了事務的一些相關參數

* TransactionStatus 表明事務運行的一個實時狀態

理解三者的關係:事務管理器經過讀取事務定義參數進行事務管理,而後會產生一系列的事務狀態

基於XML的聲明式事務控制

在 Spring 配置文件中聲明式的處理事務來代替代碼式的處理事務。底層採用AOP思想來實現的。

聲明式事務控制明確事項:

  • 核心業務代碼(目標對象) (切入點是誰?)
  • 事務加強代碼(Spring已提供事務管理器))(通知是誰?)
  • 切面配置(切面如何配置?)

快速入門

  • 需求

使用spring聲明式事務控制轉帳業務。

  • 步驟分析
1. 引入tx命名空間

2. 事務管理器通知配置

3. 事務管理器AOP配置

4. 測試事務控制轉帳業務代碼

1)引入tx命名空間

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w2.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/s chema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

</beans>

2)事務管理器通知配置

<!--事務管理器-->
<bean 
>
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!--通知加強-->
<tx:advice  transaction-manager="transactionManager">
    <!--定義事務的屬性-->
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

3)事務管理器AOP配置

<!--aop配置-->
<aop:config>
    <!--切面配置-->
    <aop:advisor advice-ref="txAdvice"
                 pointcut="execution(* com.lagou.serivce..*.*(..))">
    </aop:advisor>
</aop:config>

4)測試事務控制轉帳業務代碼

@Override
public void transfer(String outUser, String inUser, Double money) {
    accountDao.out(outUser, money);
    // 製造異常
    int i = 1 / 0;
    accountDao.in(inUser, money);
}

事務參數的配置詳解

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>

* name:切點方法名稱

* isolation:事務的隔離級別

* propogation:事務的傳播行爲

* timeout:超時時間

* read-only:是否只讀
  • CRUD經常使用配置
<tx:attributes>
    <tx:method name="save*" propagation="REQUIRED"/>
    <tx:method name="delete*" propagation="REQUIRED"/>
    <tx:method name="update*" propagation="REQUIRED"/>
    <tx:method name="find*" read-only="true"/>
    <tx:method name="*"/>
</tx:attributes>

知識小結

* 平臺事務管理器配置

* 事務通知的配置

* 事務aop織入的配置

基於註解的聲明式事務控制

經常使用註解

步驟分析

1. 修改service層,增長事務註解
2. 修改spring核心配置文件,開啓事務註解支持

步驟分析

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ, timeout = -1, readOnly = false)
    @Override
    public void transfer(String outUser, String inUser, Double money) {
        accountDao.out(outUser, money);
        int i = 1 / 0;
        accountDao.in(inUser, money);
    }
}

2)修改spring核心配置文件,開啓事務註解支持

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w2.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--省略以前datsSource、jdbcTemplate、組件掃描配置-->

    <!--事務管理器-->
    <bean  >
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--事務的註解支持-->
    <tx:annotation-driven/>
</beans>

純註解

核心配置類

@Configuration // 聲明爲spring配置類
@ComponentScan("com.lagou") // 掃描包
@Import(DataSourceConfig.class) // 導入其餘配置類
@EnableTransactionManagement // 事務的註解驅動
public class SpringConfig {

    @Bean
    public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean("transactionManager")
    public PlatformTransactionManager getPlatformTransactionManager(@Autowired DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

}

數據源配置類

@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

知識小結

* 平臺事務管理器配置(xml、註解方式)

* 事務通知的配置(@Transactional註解配置)

* 事務註解驅動的配置 <tx:annotation-driven/>、@EnableTransactionManagement
Spring集成web環境

ApplicationContext應用上下文獲取方式

應用上下文對象是經過 new ClasspathXmlApplicationContext(spring配置文件) 方式獲取的,可是每次從容器中得到Bean時都要編寫 new ClasspathXmlApplicationContext(spring配置文件) ,這樣的弊端是配置文件加載屢次,應用上下文對象建立屢次。 Version:0.9 StartHTML:0000000105 EndHTML:0000002684 StartFragment:0000000141 EndFragment:0000002644

解決思路分析: 在Web項目中,可使用ServletContextListener監聽Web應用的啓動,咱們能夠在Web應用啓動時,就加載Spring的配置文件,建立應用上下文對象ApplicationContext,在將其存儲到最大的域servletContext域中,這樣就能夠在任意位置從域中得到應用上下文ApplicationContext對象了。

Spring提供獲取應用上下文的工具

上面的分析不用手動實現,Spring提供了一個監聽器ContextLoaderListener就是對上述功能的封裝,該監聽器內部加載Spring配置文件,建立應用上下文對象,並存儲到ServletContext域中,提供了一個客戶端工具WebApplicationContextUtils供使用者得到應用上下文對象。

因此咱們須要作的只有兩件事:

  1. 在web.xml中配置ContextLoaderListener監聽器(導入spring-web座標)
  2. 使用WebApplicationContextUtils得到應用上下文對象ApplicationContext

實現

1)導入Spring集成web的座標

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>

2)配置ContextLoaderListener監聽器

<!--全局參數-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>•
</context-param>
<!--Spring的監聽器-->
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

3)經過工具得到應用上下文對象

ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Object obj = applicationContext.getBean("id");


 

相關文章
相關標籤/搜索