Spring入门【下】

news/2024/5/20 10:19:54 标签: 并发, JUC

Spring入门【三】

以转账为案例。

一、转账编码

1、引入依赖
<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
2、实体类
public class Account {

    private Integer id;
    private String name;
    private Float money;
}
3、持久层
1. 接口
package com.torlesse.dao;

import com.torlesse.domain.Account;

public interface AcountDao {
    /**
     * 根据账户名查询账户
     * @param fromName
     * @return
     */
    Account findByName(String fromName);

    /**
     * 更新账户
     * @param fromAccount
     */
    void update(Account fromAccount);
}

2. 实现类
package com.torlesse.dao.impl;

import com.torlesse.dao.AcountDao;
import com.torlesse.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.sql.SQLException;


@Repository
public class AcountDaoImpl implements AcountDao {
    @Autowired
    QueryRunner queryRunner;

    @Override
    public Account findByName(String fromName) {
        String sql = "select * from account where name = ?";
        try {
            return queryRunner.query(sql ,new BeanHandler<>(Account.class),fromName);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void update(Account account) {
        String sql = "update account set money = ? where name = ?";
        try {
            queryRunner.update(sql ,account.getMoney(), account.getName());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

4、业务层
1. 接口
package com.torlesse.service;

public interface AccountService {
    /**
     *
     * @param fromName 从哪个账户转出
     * @param toName   转入哪个账户
     * @param money     转多少钱
     */
    public void transfer(String fromName, String toName ,Float money);
}

2. 实现类
package com.torlesse.service.impl;

import com.torlesse.dao.AcountDao;
import com.torlesse.domain.Account;
import com.torlesse.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 *
 * 一个事务必须在一个Connection中完成
 *
 * ThreadLocal:线程绑定
 *   绑定Connection对象
 *   业务层和持久层需要Connection从ThreadLocal中获取
 *
 */
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AcountDao acountDao;

    @Override
    public void transfer(String fromName, String toName, Float money) {
        try {
            //事务1:开启事务:conn.setAutoCommit(false);

            //查询要转出的账户
            Account fromAccount =  acountDao.findByName(fromName);
            //查询转入的账户
            Account toAccount =  acountDao.findByName(toName);
            //修改要转出的账户余额:假设余额充足
            fromAccount.setMoney(fromAccount.getMoney() - money);
            //修改要转入的账户余额
            toAccount.setMoney(toAccount.getMoney() + money);
            //持久化到数据库
            acountDao.update(fromAccount);
            //出现异常
            System.out.println(1/0);
            acountDao.update(toAccount);
            //事务2:提交事务:conn.commit();
        } catch (Exception e) {
            //事务3:回顾事务 conn.rollback();
            e.printStackTrace();
        } finally {
            //事务4:还原状态 conn.setAutoCommit(true);

        }

        System.out.println();

    }
}

5、配置文件
<?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.torlesse"></context:component-scan>
    <!--创建queryRunner对象:构造方法中需要DataSource对象-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg type="javax.sql.DataSource" ref="dataSource"></constructor-arg>
    </bean>
    <!--创建dataSource对象:注入四个必要属性-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--引入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
</beans>
6、测试
package com.torlesse;

import com.torlesse.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransfer {

    @Autowired
    AccountService accountService;

    @Test
    public void test(){
        //创建类对象,创建springIOC容器
//        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        accountService.transfer("aaa","bbb", 100f);
    }

}

7、发现问题

二、解决转账问题

1、引入工具类
引入ConnectionUtils
package com.torlesse.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * 一个管理连接的工具类,用于实现连接和线程的绑定
 *
 * 保证当前线程中获取的Connection是同一个
 */
@Component
public class ConnectionUtil {
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    @Autowired
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    /**
     * 获取当前线程上绑定的连接
     * @return
     */
    public Connection getThreadConnection() {
        try {
            //1.先看看线程上是否绑了
            Connection conn = tl.get();
            if(conn == null) {
                //2.从数据源中获取一个连接
                conn = dataSource.getConnection();
                //3.和线程局部变量绑定
                tl.set(conn);
            }
            //4.返回线程上的连接
            return tl.get();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 把连接和当前线程解绑
     */
    public void remove() {
        tl.remove();
    }
}

引入TransactionManager
package com.torlesse.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;

/**
 * 事务管理器
 */
@Component
public class TransactionManager {

    @Autowired
    private ConnectionUtil connectionUtil;

    public void setConnectionUtil(ConnectionUtil connectionUtil) {
        this.connectionUtil = connectionUtil;
    }

    //开启事务
    public void beginTransaction() {
        //从当前线程上获取连接,实现开启事务
        try {
            connectionUtil.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //提交事务
    public void commit() {
        try {
            connectionUtil.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    //回滚事务
    public void rollback() {
        try {
            connectionUtil.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //释放连接
    public void release() {
        try {
            connectionUtil.getThreadConnection().setAutoCommit(true);
            //关闭连接(还回池中)
            connectionUtil.getThreadConnection().close();
            //解绑线程:把连接和线程解绑
            connectionUtil.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
2、修改业务层
package com.torlesse.service.impl;

import com.torlesse.dao.AcountDao;
import com.torlesse.domain.Account;
import com.torlesse.service.AccountService;
import com.torlesse.utils.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 *
 * 一个事务必须在一个Connection中完成
 *
 * ThreadLocal:线程绑定
 *   绑定Connection对象
 *   业务层和持久层需要Connection从ThreadLocal中获取
 *
 */
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AcountDao acountDao;

    @Autowired
    TransactionManager txManager;

    /**
     * 假设需要事务管理
     * @param account
     */
    public void update(Account account){
        try {
            //事务1:开启事务:conn.setAutoCommit(false);
            txManager.beginTransaction();
            acountDao.update(account);
            //事务2:提交事务:conn.commit();
            txManager.commit();
        } catch (Exception e) {
            //事务3:回顾事务 conn.rollback();
            txManager.rollback();
            e.printStackTrace();
        } finally {
            //事务4:还原状态 conn.setAutoCommit(true);
            txManager.release();
        }
    }

    @Override
    public void transfer(String fromName, String toName, Float money) {
        try {
            //事务1:开启事务:conn.setAutoCommit(false);
            txManager.beginTransaction();
            //查询要转出的账户
            Account fromAccount =  acountDao.findByName(fromName);
            //查询转入的账户
            Account toAccount =  acountDao.findByName(toName);
            //修改要转出的账户余额:假设余额充足
            fromAccount.setMoney(fromAccount.getMoney() - money);
            //修改要转入的账户余额
            toAccount.setMoney(toAccount.getMoney() + money);
            //持久化到数据库
            acountDao.update(fromAccount);
            //出现异常
            System.out.println(1/0);
            acountDao.update(toAccount);
            //事务2:提交事务:conn.commit();
            txManager.commit();
        } catch (Exception e) {
            //事务3:回顾事务 conn.rollback();
            txManager.rollback();
            e.printStackTrace();
        } finally {
            //事务4:还原状态 conn.setAutoCommit(true);
            txManager.release();
        }
    }
}

3、发现新的问题
问题
1. 重复代码
2. 代码臃肿问题
3. 技术与业务整合到一起了
解决的思路
	1. 提取重复的代码
	2. 业务层中不需要技术代码
	3. 不修改业务层源码的情况下,技术增强
	4. 使用动态代理
动态代理
	特点:随用随创建,随用随加载
	不修改原来代码的基础上,对原来的代码增强

三、动态代理

1、jdk动态代理
a. jdk动态代理: 基于接口的动态代理
b. @Test
    public void testJDKProxy(){
        //真实的对象
        NewSale newSale = new NewSaleImpl();
        //创建代理对象-- 本质上就是接口的一个实现类
            //参数1: 类加载器
            //参数2:类实现的接口
            //参数3:真实对象的增强部分:实现了InvocationHandler接口的实现类
                //匿名内部类也是该接口的实现类
        NewSale sale = (NewSale) Proxy.newProxyInstance(newSale.getClass().getClassLoader(), newSale.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 增强内容
                     * @param proxy : 代理对象
                     * @param method : 代理的方法,未增强的方法
                     * @param args  : 代理的方法的参数
                     * @return  代理的方法的返回值
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //生成产品
                        ProductFactory productFactory = new ProductFactory();
                        productFactory.make();
                        //开始销售:通过反射执行真实对象的方法
                            //参数1:真实的对象
                            //参数2:方法的参数
                        method.invoke(newSale,args );
                        //判断是否挣钱了
                        //卖的价格:args[0]  假设产品的成本是 1500
                        if( (Float)args[0] > 1500){
                            //卖的价格高于成本,挣了,可卖
                            System.out.println("卖的价格高于成本,挣了,可卖");
                        }else{
                            //卖的价格低于成本,赔了,不可卖
                            System.out.println("卖的价格低于成本,赔了,不可卖");
                        }
                        return null;
                    }
                });

        sale.sale(1000F);
    }
2、cglib动态代理
a. cglib动态代理: 基于类的动态代理
b. 第三方jar包: cglib-2.2.2.jar
c. 注意:代理的类不用final修饰
d. public void testCglibProxy(){
        //真实对象
        OldSale oldSale = new OldSale();
        //创建cglib代理对象
        //1. 创建增强类对象
        Enhancer enhancer = new Enhancer();
        //2. 指定代理对象的父类
        enhancer.setSuperclass(oldSale.getClass());
        //3. 指定增强的内容
        //MethodInterceptor  :接口是方法拦截器
        enhancer.setCallback(new MethodInterceptor() {
            /***
             *  增强的内容
             * @param o  代理对象,增强后的对象
             * @param method   代理的方法
             * @param objects   代理的方法的参数
             * @param methodProxy   代理方法,增强后的方法
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //生成产品
                ProductFactory factory = new ProductFactory();
                factory.make();
                //开始销售,执行真实对象的内容
                method.invoke(oldSale, objects);
                //判断是否挣钱了
                //卖的价格:objects[0]  假设产品的成本是 1500
                if( (Float)objects[0] > 1500){
                    //卖的价格高于成本,挣了,可卖
                    System.out.println("卖的价格高于成本,挣了,可卖");
                }else{
                    //卖的价格低于成本,赔了,不可卖
                    System.out.println("卖的价格低于成本,赔了,不可卖");
                }

                return null;
            }
        });

        //4. 创建代理对象:
        OldSale saleProxy  = (OldSale)enhancer.create();

        saleProxy.sale(2000f);
    }

五、动态代理解决新问题

1、jdk动态代理解决问题
@Test
    public void testJDKProxyService(){
        //创建业务层代理对象
        AccountService accountServiceProxy = (AccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            //开启事务
                            txManager.beginTransaction();
                            //执行真实的对象的方法
                            method.invoke(accountService, args);
                            //提交事务
                            txManager.commit();
                        } catch (Exception e) {
                            //事务回滚
                            txManager.rollback();
                            e.printStackTrace();
                        } finally {
                            //还原状态
                            txManager.release();
                        }
                        return null;
                    }
                });

        accountServiceProxy.transfer("aaa","bbb",200f);
    }
2、cglib动态代理解决问题
 @Test
    public void testCglibProxy(){
        //增强对象
        Enhancer enhancer = new Enhancer();
        //指定代理对象的父类
        enhancer.setSuperclass(accountService.getClass());
        //增强的内容
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                try {
                    txManager.beginTransaction();
                    method.invoke(accountService, objects);
                    txManager.commit();
                } catch (Exception e) {
                    txManager.rollback();
                    e.printStackTrace();
                } finally {
                    txManager.release();
                }
                return null;
            }
        });
        //创建代理对象
        AccountService accountServiceProxy = (AccountService)enhancer.create();
        accountServiceProxy.transfer("aaa","bbb",200f);
    }


四、AOP的xml配置

1、依赖
<!--引入spring的核心-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--引入spring的测试包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--引入单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--配置aop,必须引入一个包:版本必须要1.8.7以上-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
2、配置文件
<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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">
    <!--扫描表,创建bean对象-->
    <context:component-scan base-package="com.torlesse"></context:component-scan>

    <!--通知对象: 拦截到方法时,通知执行的对象-->
    <!--
        通知的类型
            前置通知: 方法之前执行
            后置通知: 方法执行完之后执行- 返回之前执行-如果有异常,则不执行
            最终通知: 方法执行完后总会执行- finally
            异常通知: 方法出现异常则执行
            环绕通知: 前置通知+后置通知+最终通知+异常通知
    -->
    <bean id="logger" class="com.torlesse.log.Logger"></bean>
    <!--配置aop-->
    <aop:config>
        <!--配置切面= 切入点 + 通知
            指定通知对象是谁
         -->
        <aop:aspect ref="logger">
            <!--配置切入点
                id:唯一的标志
                expression: 表达式

                * com.torlesse.service.impl.*.*(..)
                * com.torlesse.service..*.*(..)
                第一个*:代表方法任意返回值类型
                第二个*: 类名任意,impl包中所有的类
                第三个*: 任意方法名
                (..) : 参数任意,个数任意,类型任意,顺序任意
            其他的配置方式
                public void com.torlesse.service.impl.UserServiceImpl.findAll()
                void com.torlesse.service.impl.UserServiceImpl.findAll()
                * com.torlesse.service.impl.UserServiceImpl.findAll()
                * com.torlesse.service..UserServiceImpl.findAll() : .. 代表的是包,及其子包

            -->
            <aop:pointcut id="pointcut" expression="execution(* com.torlesse.service.impl.*.*(..))"></aop:pointcut>
            <!--织入: 告诉通知对象执行,具体执行哪一个方法-->
            <!--前置通知-->
            <!--<aop:before method="before" pointcut-ref="pointcut"></aop:before>-->
            <!--后置通知-->
            <!--<aop:after-returning method="afterReturning" pointcut-ref="pointcut"></aop:after-returning>-->
            <!--最终通知-->
            <!--<aop:after method="after" pointcut-ref="pointcut"></aop:after>-->
            <!--异常通知-->
            <!--<aop:after-throwing throwing="e" method="afterThrowing" pointcut-ref="pointcut"></aop:after-throwing>-->
            <!--环绕增强-->
            <aop:around method="around" pointcut-ref="pointcut"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

五、AOP的注解配置

package com.torlesse.log;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 *@Component: 创建类对象
 * @Aspect:配置该类为切面
 *  切面是:切入点 + 通知
 *
 */
@Component
@Aspect
public class Logger {
    /**
     * 配置切入点
     */
    @Pointcut("execution(* com.torlesse.service.impl.*.*(..))")
    public void pointcut(){};


    /**
     *
     * @param joinPoint  连接点-- 拦截到的方法
     */
//    @Before("pointcut()")
    public void before(JoinPoint joinPoint){
        //被代理的对象
        Object target = joinPoint.getTarget();
        //拦截的类的名称
        String className = target.getClass().getName();
        System.out.println("拦截到的类名:" +className);
        //获取方法对象
        Signature signature = joinPoint.getSignature();
        //获取方法名
        String methodName = signature.getName();
        System.out.println("拦截到方法名:" + methodName);
        System.out.println("前置通知");
    }

//    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("后置增强");
    }

//    @After("pointcut()")
    public void after(){
        System.out.println("最终增强");
    }

//    @AfterThrowing(value = "pointcut()",throwing = "e")
    public void afterThrowing(Exception e){
        System.out.println("执行的方法的异常:"+e);
        System.out.println("异常通知");
    }

    /**
     * ProceedingJoinPoint 可以执行拦截到方法的连接点对象
     * @param joinPoint
     */
    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("前置通知");
            //执行原来的方法: 可以获取方法的返回值
            Object result = joinPoint.proceed();
            System.out.println("后置增强");
        } catch (Throwable e) {
            System.out.println("异常通知");
//            e.printStackTrace();
        } finally {
            System.out.println("最终增强");
        }
    }
}

Spring入门【四】

一、jdbcTemplate的使用

1、jdbcTemplate的介绍
jdbc --- dbutils -- jdbcTemplate(spring 提供) -- mybatis(主流) -- spring data jpa(趋势)
2、数据源配置
a. c3p0数据源
		<!--c3p0数据源-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
	 <!--引入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--配置c3p0数据源-->
    <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
b. dbcp 数据源
	<!--dbcp数据源-->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
		<!--dbcp数据源-->
    <bean id="dbcpDataSource"  class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
c. spring jdbc 自带数据源,包含了JdbcTemplate对象
	<!--spring自带数据源-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
 <!--spring自带的数据源-->
    <bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
3、jdbcTemplate的CRUD
package com.torlesse;

import com.torlesse.domain.Account;
import com.torlesse.mapper.AccountRowMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
 * 查询使用query
 * 增删改:update
 *
 * queryForList :查询返回一个List集合,Map集合
 * query(sql ,属性与列的映射对象,参数):返回值:List<pojo>
 * queryForObject :针对于返回一个对象
 * update(sql ,参数):执行增删改操作
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Test
    public void testFindAll(){
        String sql = "select * from account";
//        方法返回值是List集合,list中是map、集合
//        List<Map<String, Object>> accountList = jdbcTemplate.queryForList(sql);
//        for (Map<String, Object> map : accountList) {
//            System.out.println(map);
//        }
        List<Account> accountList = jdbcTemplate.query(sql, new AccountRowMapper());
        for (Account account : accountList) {
            System.out.println(account);
        }

    }

    @Test
    public void testFindById(){
        String sql = "select * from account where id = ?";
//        List<Account> accountList = jdbcTemplate.query(sql, new AccountRowMapper(), 5);
//        System.out.println(accountList.size() == 1 ?accountList.get(0):"结果为null");
//        queryForObject(SQL语句,列与属性的映射, 参数):掌握
        Account account = jdbcTemplate.queryForObject(sql, new AccountRowMapper(), 5);
        System.out.println(account);
//        queryForObject(sql语句,参数列表(必须是数组类型), jdbcTemplate 自带映射:必须保证属性和列名一致)
//        Account account = jdbcTemplate.queryForObject(sql, new Object[]{1}, new BeanPropertyRowMapper<>(Account.class));
//        System.out.println(account);

    }

    @Test
    public void testSave(){
        String sql = "insert into account values(null , ? ,?)";
        jdbcTemplate.update(sql ,"zhangsan", 10000);
    }

    @Test
    public void testUpdate(){
        String sql = "update account set money = ?, name = ? where id = ?";
        jdbcTemplate.update(sql ,1000,"lisi",4);
    }

    @Test
    public void testDel(){
        String sql = "delete from account where id = ?";
        jdbcTemplate.update(sql , 4);
    }

    @Test
    public void testGetTotalCount(){
        String sql = "select count(*) from account";
//        queryForObject(sql语句, 返回值类型)
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println(count);
    }
}

4、在dao中使用jdbcTemplate方法一
a. applicationContext.xml
		<!--创建jdbcTemplate模板对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="springDataSource"></property>
    </bean>
b.在dao层中使用模板对象
@Repository
public class AccountDaoImpl implements AccountDao {

	  //自动注入模板对象
    @Autowired
    JdbcTemplate  jdbcTemplate;
    
}
5、在dao中使用jdbcTemplate方法二
a. 在dao实现类中继承接口JdbcDaoSupport类
	public class AccountDaoImpl2 extends JdbcDaoSupport implements AccountDao {
b.所有的dao对象需要在xml中创建,需要通过set方法注入数据源对象
		<!--创建dao层对象-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl2">
        <!--通过set方法注入数据源-->
        <property name="dataSource" ref="springDataSource"></property>
    </bean>

二、spring的事务控制

1、spring事务控制的api介绍–事务管理类
org.springframework.orm.hibernate5.HibernateTransactionManager: 在hibernate环境下使用
rg.springframework.jdbc.datasource.DataSourceTransactionManager: 在jdbcTemplate,mybatis(ibatis)环境下使用
2、事务的特性
a、事务的四个特性
	原子性: 不可再分割
	隔离性: 事务之间的隔离级别
	一致性: 要么全部完成,要么全部不完成
	持久性: 一旦提交持久化到数据中
b、隔离级别
	读未提交:read uncommited
		产生的问题:脏读,幻读,不可重复读
		脏读:读到了未提交的数据
		不可重复读:
		幻读(虚读):
	读已提交:read commited
		产生的问题:幻读,不可重复读
		解决的问题:脏读
	重复读:  repeatable read
		产生的问题:幻读
		解决的问题:脏读,不可重复读
	串行化(序列化): serializable 
		产生的问题:null
		解决的问题: 所有的问题
		隔离级别最高,效率最低
c、数据库的支持的隔离级别 -- 一般情况下选择都是默认的隔离级别
	mysql:支持:read uncommited  read commited  repeatable read  serializable  支持三个隔离级别
	默认的隔离级别:repeatable read
	Oracle支持:read commited   serializable   read only(只读)
	默认的隔离级别:read commited 

3、事务的传播
a. 掌握
REQUIRED: 必要的: 如果没有事务,则新建一个事务,如果有事务,加入这个事务当中, spring指定为默认值
	增删改:
SUPPORTS: 支持的: 如果没有事务,非事务执行,如果有事务,加入这个事务当中
	查询
b. 了解
MANDATORY: 可以使用当前的事务,如果没有事务,抛出异常
REQUERS_NEW: 新建一个事务,如果存在事务,则挂起事务
NOT_SUPPORTED: 必须非事务执行,如果有事务,则挂起事务
NEVER: 非事务执行,如果存在事务,抛出异常
NESTED: 有事务,嵌套执行,没有事务,执行REQUIRED
4、是否为只读的事务
a.如果是查询,则为只读的事务  readOnly=true
b.如果是增删改,则为非只读的事务,readOnly=false=
5、基于xml声明式事务管理(配置文件)(重点)(推荐)
a. 编程式事务管理:在业务层写了事务技术代码
b. 声明式事务管理:在配置文件声明事务对象,管理事务,业务层中没有任何事务代码
1) 引入依赖:
			<!--aop的切面配置需要的jar包-->
      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.9</version>
      </dependency>
      <!--spring 的事务管理jar包-->
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.0.2.RELEASE</version>
      </dependency>
      <!--spring 的基础包-->
			<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
      <!--spring自带数据源, 包含了事务管理类 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--两个测试包 -->
         <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
 2) 编写配置文件
 <!--创建事务管理器对象-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源属性:事务存在连接中- 连接存在连接池(数据源)中-->
        <property name="dataSource" ref="springDataSource"></property>
    </bean>

    <!--事务的增强: 过滤方法是否需要拦截
        id:唯一的标识
        transaction-manager:指定事务管理器
    -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!--增强:方法的过滤-->
        <tx:attributes>
            <!--指定需要拦截的方法
            isolation:隔离级别, 一般选择默认的
            propagation:传播的行为,
            read-only: 是否为只读的事务,增删改:非只读事务 read-only=false
                                        查询:只读的事务:read-only=true


            find* :通配符配置,只要以 find开头即可
            -->
         <!--配置方式一:-->
            <!--增删改-->
            <!--<tx:method name="insert*" />-->
            <!--<tx:method name="add*" />-->
            <!--<tx:method name="update*" />-->
            <!--<tx:method name="del*" />-->
            <!--<tx:method name="delete*" />-->
            <!--<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>-->
            <!--查询-->
            <!--<tx:method name="find*"  propagation="SUPPORTS" read-only="true" ></tx:method>-->
            <!--<tx:method name="select*" propagation="SUPPORTS" read-only="true" ></tx:method>-->
            <!--<tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>-->
            <!--<tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"></tx:method>-->
       <!--配置方式二-->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>
            <!--其他方法的配置方法-->
            <tx:method name="*" propagation="REQUIRED" read-only="false"></tx:method>
        </tx:attributes>
    </tx:advice>
    <!--aop的配置:
        切面= 切入点 +通知(增强)
        -->
    <aop:config>
        <!--切面配置
            advice-ref: 通知关联对象
            pointcut: 切入点:
          -->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.torlesse.service.impl.*.*(..))"></aop:advisor>
    </aop:config>
 3) 业务层
 	业务层不需要任何事务管理,只需要提供业务代码即可
6、基于注解的配置(重点)
a. 引入依赖
	与xml完全一致
b. 配置文件
	1)配置事务管理类
	 <!--创建事务管理器对象-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源属性:事务存在连接中- 连接存在连接池(数据源)中-->
        <property name="dataSource" ref="springDataSource"></property>
    </bean>
  2) 开启事务的注解管理
  	<!--transaction-manager: 关联事务管理器对象-->
    <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>

    <!--开启aop的注解:自动代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
 c. 业务层
 	在类上标记注解:@Transactional, 类中所有的方法都会使用事务管理
 	在方法上标记注解:@Transactional:只有该方法按照事务执行
 d.属性介绍
 @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false,timeout = -1)

书籍推荐

以下是一些关于Spring框架的书籍推荐:

  1. 《Spring揭秘》:这本书是一本经典的Spring入门书籍,适合想要深入了解Spring框架的开发者。
  2. 《精通Spring 4.x》:这本书以实战为主,通过实例讲解了Spring框架的各种组件,适合想要快速掌握Spring框架的开发者。
  3. 《Spring Boot实战》:这本书介绍了如何使用Spring Boot快速构建Web应用程序,适合想要学习如何使用Spring Boot开发的开发者。
  4. 《Spring核心技术》:这本书系统地介绍了Spring框架各个基本组件的基本使用,适合想要全面了解Spring框架的开发者。

希望对你有所帮助~


http://www.niftyadmin.cn/n/470581.html

相关文章

力控机器人力控制

力控制(Force Control)是一种机器人或机械系统的控制策略,旨在实现对施加在系统末端执行器上的力或力矩进行精确控制。它强调对外部力的感知和调整,使得机器人能够与环境或人类进行交互,并完成特定的任务。 力控制可以用于控制施加在机器人末端执行器上的各种类型的力,包…

string类的用法

1、string 类的构造函数 string(const char *s) &#xff1a;将 string 对象初始化为 s 指向的字符串string str("Hello!");string()&#xff1a;创建一个默认的 string 对象&#xff0c;长度为 0&#xff08;默认构造函数&#xff09;string str;     // 创建一…

提前出击:如何在故障降临之前解决设备问题?

在现代工业生产中&#xff0c;设备故障和停机时间对企业来说是极具挑战性和成本高昂的问题。为了解决这一问题&#xff0c;预测性维护作为一种先进的维护策略应运而生。本文将探讨预测性维护的概念以及如何通过它在设备故障之前解决问题。 预测性维护是一种基于设备运行数据和分…

查询穿梭框实现

技术栈&#xff1a; 使用vue3 的composition api 和 tsx 进行开发 一、需求描述 点击编辑&#xff0c;显示穿梭框&#xff1a; 可将左侧维度拖动至右侧&#xff0c;已选维度可上下拖动调整顺序。 二、需求分析 1、状态传递过程 我们首先来分析一下&#xff0c;整个流程中的…

java中的Lambda表达式和方法引用区别和联系,用代码实例去说明

Java中的Lambda表达式和方法引用都是函数式编程的语法特性&#xff0c;它们可以用于简化代码&#xff0c;提高可读性和可维护性。下面是它们的区别和联系&#xff1a; 语法形式不同&#xff1a;Lambda表达式使用“->”符号来连接参数和方法体&#xff0c;而方法引用使用“::…

【adb 命令】

1.ADB概念 ADB&#xff0c;全名 Android Debug Bridge&#xff0c;是 Android 提供的一个通用的调试工具&#xff0c;是一个 C/S 架构的命令行工具&#xff0c;通过这个工具&#xff0c;使得我们的 PC 能够和 Android 设备来进行通信。 1.1 ADB的工作原理&#xff1a; adb 主要…

Zookeeper源码解析(上)

序&#xff1a; Zookeeper最早起源于雅虎研究院的一个研究小组&#xff0c; 在当时&#xff0c; 研究人员发现&#xff0c;在雅虎内部有很大大型的系统都是需要一个依赖一个类似的系统来进行分布式协调&#xff0c;但是在系统往往都存在分布式单点问题&#xff0c;所以雅虎的开…

数据结构之复杂度分析

1、大 O 复杂度表示法 算法的执行效率&#xff0c;粗略地讲&#xff0c;就是算法代码执行的时间 这里有段非常简单的代码&#xff0c;求 1,2,3…n 的累加和。看如何来估算一下这段代码的执行时间 int cal(int n) {int sum 0;int i 1;for (; i < n; i) {sum sum i;}ret…