spring 框架學習-4

spring 框架學習安排

1. 第四天

1. spring中的JdbcTemplate

1.JdbcTemplate的CRUD基本使用
  1. JdbcTemplate的作用:與數據庫進行交互,進行CRUD操作
  2. 最簡單的demo
public class JDBCTemplateDemo1 {

    public static void main(String[] args) {
//        使用spring自帶的數據源
        DriverManagerDataSource dmds = new DriverManagerDataSource();
        dmds.setDriverClassName("com.mysql.jdbc.Driver");
        dmds.setUsername("root");
        dmds.setPassword("809080");
        dmds.setUrl("jdbc:mysql://localhost:3306/eesy");
        JdbcTemplate template = new JdbcTemplate(dmds);
        template.execute("insert into account(name,money)values('yyy',4000)");
    }
}
  1. 發現問題,有多個setXXX的出現,可以使用IOC來注入,添加bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="ds"></property>
    </bean>

    <!--配置datasource-->
    <bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="809080"></property>
    </bean>
</beans>

重新測試上述demo

/**
 * Created by liuzeyu on 2020/4/25.
 * 使用spring IOC
 */
public class JDBCTemplateDemo2 {

    public static void main(String[] args) {
        //使用IOC
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");
        jdbcTemplate.execute("delete from account where name like '%y%'");
    }
}
  1. 進行CRUD操作
/**
 * Created by liuzeyu on 2020/4/25.
 * JdbcTemplate 的 GRUD操作
 */
public class JDBCTemplateDemo3 {

    public static void main(String[] args) {
        //使用IOC
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");
        //1. 對數據庫插入一條數據
        //jdbcTemplate.update("insert into account (name,money)values(?,?)","liuzeyu",34444);
        //2. 修改一條數據
        //jdbcTemplate.update("update account set money=8888 where name=?","liuzeyu");
        //3. 刪除一條數據
        //jdbcTemplate.update("delete from account where name like ?","%liuz%");
        //4. 查詢所有
        //List<Account> accounts = jdbcTemplate.query("select * from account where money > ?", new subMapper(), 100);
//        List<Account> accounts = jdbcTemplate.query("select * from account where money > ?",
//                new BeanPropertyRowMapper<Account>(Account.class), 100);
//        for (Account account : accounts) {
//            System.out.println(account);
//        }
        //5. 查詢一個
//        List<Account> query = jdbcTemplate.query("select * from account where money > ?",
//                new BeanPropertyRowMapper<Account>(Account.class), 1000);
//        System.out.println(query.isEmpty() ? "查詢爲空" :query.get(0));
        //6. 查詢一個
        Integer count = jdbcTemplate.queryForObject("select count(*) from account where money >?", Integer.class, 0);
        System.out.println(count);

    }
}

class subMapper implements RowMapper{

    public Account mapRow(ResultSet resultSet, int i) throws SQLException {
        Account account = new Account();
        account.setId(resultSet.getInt("id"));
        account.setMoney(resultSet.getFloat("money"));
        account.setName(resultSet.getString("name"));
        return account;
    }
}

其中
在這裏插入圖片描述
所以我們通常使用spring自帶的

new BeanPropertyRowMapper<T>(T.class)
2.JdbcTemplate的DAO層使用
  1. dao層代碼
public interface IAccountDao {

    //根據id查詢
    public Account findAccountById(Integer id);

    //根據name查詢
    public Account findAccountByName(String name);

}
public class AccountDaoImpl implements IAccountDao {
    private JdbcTemplate jdbcTemplate = null;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public Account findAccountById(Integer id) {
        List<Account> accounts = jdbcTemplate.query("select * from account where id=?",
                new BeanPropertyRowMapper<Account>(Account.class), id);
        Account  account = accounts.isEmpty() ?null:accounts.get(0);
        return account;
    }

    public Account findAccountByName(String name) {
        List<Account> accounts = jdbcTemplate.query("select * from account where name =?",
                new BeanPropertyRowMapper<Account>(Account.class), name);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size() > 1){
            throw new RuntimeException("查詢結果不唯一");
        }
        return accounts.get(0);
    }

}

  1. bean.xml添加jdbcTemplate注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置accountDao-->
    <bean id="accountDao" class="com.liuzeyu.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="ds"></property>
    </bean>

    <!--配置datasource-->
    <bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="809080"></property>
    </bean>
</beans>
  1. 測試方法
public class JDBCTemplateDemo4 {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountDao accountDao = ac.getBean("accountDao", IAccountDao.class);
        Account byId = accountDao.findAccountById(1);
        //System.out.println(byId);
        Account byName = accountDao.findAccountByName("ccc");
        //System.out.println(byName);
    }
}
3. Spring提供JdbcDaoSupport的支持

2中Dao層存在的問題:如果有多個dao,則下面代碼將重複出現多次
在這裏插入圖片描述
如何解決這一個問題呢?我們可以定義一個類,並把重複的代碼抽取出來

public class DaoSupport {

    public JdbcTemplate jdbcTemplate = null;

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate = new JdbcTemplate();
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void setDataSource(DataSource source){
        if(jdbcTemplate == null)
            jdbcTemplate = new JdbcTemplate(source);
    }
    public JdbcTemplate createJdbcTemplate(DataSource source) {
        return new JdbcTemplate(source);
    }

}

在這裏插入圖片描述
那如何獲取這個datasource呢?可以注入
因爲dao的實現類繼承了該方法,那它一定繼承了該方法的setDataSource方法,於是可以注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置accountDao-->
    <bean id="accountDao" class="com.liuzeyu.dao.impl.AccountDaoImpl">
        <!--<property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
        <property name="dataSource" ref="ds"></property>
    </bean>

    <!--配置JdbcTemplate已經不需要了-->
    <!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">-->
        <!--<property name="dataSource" ref="ds"></property>-->
    <!--</bean>-->

    <!--配置datasource-->
    <bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="809080"></property>
    </bean>
</beans>

dao層實現類的使用可以直接super.jdbcTemplate得到
當然。spring這麼強大,已經爲我們提供了一個DaoSupport 來處理多處編寫jdbcTemplate的問題。也是可以以上面注入的相同方式得到jdbcTemplate

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao 

2. 作業:spring中基於AOP的事務控制

github鏈接
說明:註解配置和老師的演示結果有所不同,後期又必要深入研究。

4. spring中的事務控制

1. 基於XML的聲明式事務控制
  1. 準備工程

    1. dao層代碼
    /**
     * 賬戶的持久層接口
     */
    public interface IAccountDao {
    
        /**
         * 根據Id查詢賬戶
         * @param accountId
         * @return
         */
        Account findAccountById(Integer accountId);
    
        /**
         * 根據名稱查詢賬戶
         * @param accountName
         * @return
         */
        Account findAccountByName(String accountName);
    
        /**
         * 更新賬戶
         * @param account
         */
        void updateAccount(Account account);
    }
    
    /**
     * 賬戶的持久層實現類
     */
    public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    
    
        public Account findAccountById(Integer accountId) {
            List<Account> accounts = super.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
            return accounts.isEmpty()?null:accounts.get(0);
        }
    
    
        public Account findAccountByName(String accountName) {
            List<Account> accounts = super.getJdbcTemplate().query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
            if(accounts.isEmpty()){
                return null;
            }
            if(accounts.size()>1){
                throw new RuntimeException("結果集不唯一");
            }
            return accounts.get(0);
        }
    
        public void updateAccount(Account account) {
            super.getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
        }
    }
    
    
    1. service層代碼
    package com.liuzeyu.service;
    
    import com.liuzeyu.domain.Account;
    
    /**
     * 賬戶的業務層接口
     */
    public interface IAccountService {
        /**
         * 根據id查詢賬戶信息
         * @param accountId
         * @return
         */
        Account findAccountById(Integer accountId);
    
        /**
         * 轉賬
         * @param sourceName    轉成賬戶名稱
         * @param targetName    轉入賬戶名稱
         * @param money         轉賬金額
         */
        void transfer(String sourceName, String targetName, Float money);
    }
    
    
    /**
     * 賬戶的業務層實現類
     *
     * 事務控制應該都是在業務層
     */
    public class AccountServiceImpl implements IAccountService{
    
        private IAccountDao accountDao;
    
        public void setAccountDao(IAccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
    
        public Account findAccountById(Integer accountId) {
            return accountDao.findAccountById(accountId);
    
        }
        public void transfer(String sourceName, String targetName, Float money) {
            System.out.println("transfer....");
                //2.1根據名稱查詢轉出賬戶
                Account source = accountDao.findAccountByName(sourceName);
                //2.2根據名稱查詢轉入賬戶
                Account target = accountDao.findAccountByName(targetName);
                //2.3轉出賬戶減錢
                source.setMoney(source.getMoney()-money);
                //2.4轉入賬戶加錢
                target.setMoney(target.getMoney()+money);
                //2.5更新轉出賬戶
                accountDao.updateAccount(source);
    
                int i = 1/0;
    
                //2.6更新轉入賬戶
                accountDao.updateAccount(target);
        }
    }
    
    
    1. 實體類
    package com.liuzeyu.domain;
    
    import java.io.Serializable;
    
    /**
     * 賬戶的實體類
     */
    public class Account implements Serializable {
    
        private Integer id;
        private String name;
        private Float money;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Float getMoney() {
            return money;
        }
    
        public void setMoney(Float money) {
            this.money = money;
        }
    
        @Override
        public String toString() {
            return "Account{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", money=" + money +
                    '}';
        }
    }
    
    
    1. pom.xml & bean.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>com.liuzeyu12a</groupId>
        <artifactId>day04_spring_txXML</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.6</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.0.2.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.0.2.RELEASE</version>
            </dependency>
        </dependencies>
    
    </project>
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--配置accountDao-->
        <bean id="accountDao" class="com.liuzeyu.dao.impl.AccountDaoImpl">
            <property name="dataSource" ref="ds"></property>
        </bean>
    
        <bean id="accountService" class="com.liuzeyu.service.impl.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"></property>
        </bean>
    
        <!--配置datasource-->
        <bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
            <property name="username" value="root"></property>
            <property name="password" value="809080"></property>
        </bean>
    </beans>
    
    1. 測試函數
    /**
     * 使用Junit單元測試:測試我們的配置
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:bean.xml")
    public class AccountServiceTest {
    
        @Autowired
        private  IAccountService as;
    
        @Test
        public  void testTransfer(){
            as.transfer("aaa","bbb",100f);
    
        }	
    }
    
  2. 開始配置基於XML的聲明式事務控制

    1. 需要導入aspect的依賴
     <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
         <version>1.8.7</version>
     </dependency>
    
    1. 在原有的bean.xml基礎上配置事務
    <!--spring中基於XML的聲明式事務控制(我們不用再自己寫事務管理器類和方法)
        1.配置事務管理器
        2.配置事務管理器通知(此時我們需要引入事務管理器約束+tx名稱空間和約束)同時也需要aop
            使用tx:advice配置事務管理器通知
                屬性:
                    id:給事務通知起一個唯一標識
                    transaction:給事務通知引用一個事務管理器
            3.配置aop的通用切入點表達式
    -->
    <!--1.配置事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="ds"></property>
    </bean>
    <!--2.配置事務管理器通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--2.1配置事務的屬性
             isolation="" :用於指定事務的隔離級別。默認爲default,表示使用數據庫的隔離級別
             rollback-for="":表示用於指定一個異常,當產生該異常時,回滾事務,產生其它異常時,事務不回滾。沒有默認值表示任何異常都回滾。
             no-rollback-for="":表示用於指定一個異常,當產生該異常時,不回滾事務,產生其它異常時,事務回滾,沒有默認值表示任何異常都回滾。
             propagation="":用於指定事務的傳播行爲。默認值時REQUIRED,用於增刪改的選擇。查詢方法使用SUPPORT
             read-only="":用於指定事務是否只讀,只有查詢方法才能涉及爲true,默認爲false,表示讀寫
             timeout="":用於指定事務的超時時間,默認爲-1表示永不超時,如果指定了時間,以秒爲單位
        -->
    
        <tx:attributes>
            <!--增刪改-->
            <tx:method name="transfer" propagation="REQUIRED" read-only="false"/>
            <!--查詢方法-->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
        </tx:attributes>
    </tx:advice>
    
    <!--3.配置aop-->
    <aop:config>
        <!--3.1配置aop的通用切入點表達式-->
        <aop:pointcut id="pt" expression="execution(* com.liuzeyu.service.impl.*.*(..))"></aop:pointcut>
    
        <!--3.2建立切入點表達式和事務通知的對應關係-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>
    
2. 基於註解的聲明式事務控制(基於上個項目)
  1. 將dao層和service層的實習類加入IOC容器中
@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService{...}
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
    @Autowired
    private JdbcTemplate template = null;

由於使用了註解配置,因此要想獲取template的值只能從bean.xml重新將JdbcTemplate 添加到IOC容器中,然後使用@Autowired注入

  1. 修改bean.xml
<?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:tx="http://www.springframework.org/schema/tx"
       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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd

        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--創建IOC容器要掃描的包-->
    <context:component-scan base-package="com.liuzeyu"></context:component-scan>

    <!--導入jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="ds"></property>
    </bean>

    <!--配置datasource-->
    <bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="809080"></property>
    </bean>

    <!--spring中基於註解事務控制(我們不用再自己寫事務管理器類和方法)
        1.配置事務管理器
        2.開啓spring對註解事務的支持
    -->
    <!--1.配置事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="ds"></property>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
  1. 對業務層引入事務控制,添加註解@Transactional
@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService{...}

@Transactional表示屬性去默認值,也可以加到方法上,但如果遇到不同的方法,我們必須指定不同的屬性,於是需要配置多次,這是比xml配置繁瑣的。

2. 基於純註解的聲明式事務控制
  1. 無需修改dao層和service層的代碼,添加配置類
@Configuration
@ComponentScan(basePackages = "com.liuzeyu")  //掃描包
@EnableTransactionManagement   //開啓事務管理
@Import(value = {JdbcConfiguration.class,TransactionConfiguration.class}) //子配置類
@PropertySource("jdbc.properties")   //引入外部資源文件
public class SpringConfiguration {
}
  1. 將上面的bean.xml配置文件使用註解進行配置

資源文件:

jdbc.url=jdbc:mysql://localhost:3306/eesy
jdbc.username=root
jdbc.password=809080
jdbc.driver=com.mysql.jdbc.Driver

JdbcConfiguration 子配置類

/**
 * Created by liuzeyu on 2020/4/26.
 */
public class JdbcConfiguration {

    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Value("${jdbc.driver}")
    private String driver;

    @Bean("jdbcTemplate")//將JdbcTemplate對象加入到容器中
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    @Bean("dataSource") //將DriverManagerDataSource 對象加入到容器中
    public DataSource createDataSouruce(){
        DriverManagerDataSource dataSource  = new DriverManagerDataSource();
        dataSource.setUrl(url);
        dataSource.setPassword(password);
        dataSource.setUsername(username);
        dataSource.setDriverClassName(driver);
        return dataSource;
    }
}

事務管理類配置

public class TransactionConfiguration {

    @Bean("transactionManager")  //將PlatformTransactionManager對象加入到容器中
    public PlatformTransactionManager createTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章