写点什么

Spring 入门后半部分 ----JDBCTemplate 和事务控制

用户头像
极客good
关注
发布于: 刚刚




查询单条数据




ApplicationContext app = new ClassPathXmlApplicationContext("appOfDao.xml");


JdbcTemplate jt = app.getBean(JdbcTemplate.class);


String sql="SELECT MAX(money) FROM account";


//无论是返回单个数据还是单个对象,都是调用 queryForObject


Integer moneyMax = jt.queryForObject(sql, Integer.class);


System.out.println("最高工资为:"+moneyMax);





使用带有具名参数的 sql 语句插入一条员工记录,并以 Map 的形式传入参数值




具名参数: (具有名字的参数,参数不是一个占位符了,而是以个变量名)


语法格式 --------------------> :参数名


需要使用 spring 的一个支持具名参数的 springTemplate 类


占位符参数: ?的顺序不能乱,传参的时候要注意

以 map 的形式传入参数

代码:


ApplicationContext app = new ClassPathXmlApplicationContext("appOfDao.xml");


JdbcTemplate jt = app.getBean(JdbcTemplate.class);


NamedParameterJdbcTemplate npjt = app.getBean(NamedParameterJdbcTemplate.class);


String sql="insert into account(name,money) values(:name,:money)";


//将有具名参数的值都放在 map 容器中


Map<String, Object> Map = new HashMap<String,Object>();


Map.put("name","大忽悠");


Map.put("money",8000);


int row = npjt.update(sql,Map);


System.out.println("影响的行数:"+row);




以 SqlParameterSource 的形式传入参数

使用该方法前,要确保自定义类中有 get 方法,因为该方法实现原理是从传入的对象中,找对象的 get 方法,去掉 get,首字母小写,看得到的字符串是否和具名参数匹配.

ApplicationContext app = new ClassPathXmlApplicationContext("appOfDao.xml");


JdbcTemplate jt = app.getBean(JdbcTemplate.class);


NamedParameterJdbcTemplate npjt = app.getBean(NamedParameterJdbcTemplate.class);


String sql="insert into account(name,money) values(:name,:money)";


friend f=new friend();


f.setMoney(20000);


f.setName("小朋友");


int row = npjt.update(sql, new BeanPropertySqlParameterSource(f));


System.out.println("影响的行数"+row);




使用注解完成对 JdbcTemplate 的注入----小规模常用

test 类:


@Component("test")


public class test {


@Autowired //按照类型注入


private JdbcTemplate jdbcTemplate;


int getJBDCTemplate(friend f)


{


int row = jdbcTemplate.update("insert into account values(?,?)", f.getName(), f.getMoney());


return row;


}


}


jdbc 测试类:


ApplicationContext app = new ClassPathXmlApplicationContext("appOfDao.xml");


test t=(test)app.getBean("test");


friend f=new friend();


f.setName("超级大忽悠");


f.setMoney(40000);


int row = t.getJBDCTemplate(f);


System.out.println("影响的行数"+row);


配置文件:


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


<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">


<property name="driverClass" value="${jdbc.driver}"/>


<property name="jdbcUrl" value="${jdbc.url}"/>


<property name="user" value="${jdbc.username}"/>


<property name="password" value="${jdbc.password}"/>


</bean>


<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">


<property name="dataSource" ref="dataSource"/>


</bean>





声明式事务


====================================================================


数据库环境搭建—账户表,图书表,图书库存表




account 账号表:



book 图书表:



book_stock 图书库存表:



jdbc.properties 的配置文件




jdbc.driver=com.mysql.jdbc.Driver


jdbc.url=jdbc:mysql://localhost:3306/tx


jdbc.username=root


jdbc.password=126433


数据源配置并直接注入到 jdbcTemplate 中




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


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


<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">


<property name="driverClass" value="${jdbc.driver}"/>


<property name="jdbcUrl" value="${jdbc.url}"/>


<property name="user" value="${jdbc.username}"/>


<property name="password" value="${jdbc.password}"/>


</bean>


<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">


<property name="dataSource" ref="dataSource"/>


</bean>


Dao 层和 Service 层的类环境搭建




BookDao 类:


@Repository


public class BookDao {


@Autowired


private JdbcTemplate jdbcTemplate;


/减去某个用户的剩余金额/


public void updateBalance(String username,int price)


{


String sql="update account set money=money-? where name=?";


jdbcTemplate.update(sql,price,username);


}


/获取某本图书的价格/


public int getBookPrice(String isbn)


{


String sql="select price from book where ISBN=?";


return jdbcTemplate.queryForObject(sql, Integer.class,isbn);


}


/减去某本书库存/


public void updateStock(String isbn)


{


String sql="update book_stock set stock=stock-1 where isbn=?";


jdbcTemplate.update(sql,isbn);


}


}


Service 类:


@Service


public class BookService {


@Autowired


BookDao bookDao;


/结账/


public void checkOut(String username,String isbn)


{


//1.减去库存


bookDao.updateStock(isbn);


//2.获取图书的价格


int bookPrice = bookDao.getBookPrice(isbn);


//3.减去余额


bookDao.updateBalance(username,bookPrice);


}


}


主类:


public class test {


static ApplicationContext ioc= new ClassPathXmlApplicationContext("appOfDao.xml");


public static void main(String[] args) {


BookService bs = ioc.getBean(BookService.class);


bs.checkOut("大忽悠","ISBN_001");


System.out.println("结账成功");


}


}





声明式事务


====================================================================


事务管理器(事务切面)




Spring 只是个容器,因此它并不做任何事务的具体实现。他只是提供了事务管理的接口 PlatformTransactionManager,具体内容由就由各个事务管理器来实现。



事务管理器可以在目标方法运行前后进行事务控制


目前使用 DataSourceTransactionManager

第一步:配置事务管理器,让其进行事务控制

第二步: 开启基于注解的事务控制模式,依赖 tx 命名空间


<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">


<property name="dataSource" ref="dataSource"/>


</bean>


<tx:annotation-driven transaction-manager="transactionManager"/>

第三步:给事务方法加上注解即可

加上注解之后,如果事务方法里面出现异常,那么整个事务方法会进行回滚,数据恢复原样




@Transactional 注解里面的属性分析



timeout---->参数值为 int(秒为单位),超时,事务超出指定执行时长后自动终止并回滚

@Transactional(timeout = 3)


public void checkOut(String username,String isbn)


{


//1.减去库存


bookDao.updateStock(isbn);


//2.获取图书的价格


int bookPrice = bookDao.getBookPrice(isbn);


//3.减去余额


bookDao.updateBalance(username,bookPrice);


}



readOnly---->参数值为 bool,设置事务为只读,可以进行事务优化,默认 readOnly=false,改为 readOnly=true 后,可以加快查询速度,因此不用管事务的相关操作了(设置自动提交…)

如果事务方法中有增删改相关操作,还设置为 true 时,运行时会报错

/结账/


@Transactional(readOnly = false)


public void checkOut(String username,String isbn)


{


//1.减去库存


bookDao.updateStock(isbn);


//2.获取图书的价格


int bookPrice = bookDao.getBookPrice(isbn);


//3.减去余额


bookDao.updateBalance(username,bookPrice);


}



异常分类

noRollbackFor---->参数值为 Class[] (字节码文件类型,是个数组) ,那些异常事务可以不回滚

noRollbackForClassName---->参数值为 String[] (全类名) ,那些异常事务可以不回滚

可以让原来默认回滚的异常给它不回滚


@Transactional(noRollbackFor ={ArithmeticException.class,NullPointerException.class} )


//数学异常不回滚,空指针异常不回滚


public void checkOut(String username,String isbn)


{


//1.减去库存


bookDao.updateStock(isbn);


//2.获取图书的价格


int bookPrice = bookDao.getBookPrice(isbn);


//3.减去余额


bookDao.updateBalance(username,bookPrice);


}



rollbackFor---->参数值为 Class[] (字节码文件类型) ,哪些异常事务需要回滚

rollbackForClassName---->参数值为 String[] (全类名),哪些异常事务需要回滚

原本不回滚的异常指定让其回滚,原本编译时异常不会回滚


@Transactional(rollbackFor = {FileNotFoundException.class})


//文件异常回滚


public void checkOut(String username,String isbn)


{


//1.减去库存


bookDao.updateStock(isbn);


//2.获取图书的价格


int bookPrice = bookDao.getBookPrice(isbn);


//3.减去余额


bookDao.updateBalance(username,bookPrice);


}



ioslation 调整隔离级别

前置知识:数据库事务并发问题




同时对一个数据进行修改(并发修改同一个数据下的排队,挨个按照顺序进行修改操作)

前置知识:隔离级别






前置知识:查询 mysql 的隔离级别

前置知识:事务操作

前置知识:修改 mysql 的隔离级别

例如:


set [session|global] transaction isolation level read uncommitted



有事务的业务逻辑,容器中保存的是这个业务逻辑的代理对象(了解即可)




事务传播行为(事务的传播和事务的行为)




如果有多个事务同时进行嵌套运行,子事务是否要和大事务共同用一个事务





简单理解: 一个事务相等于一辆车,如果子事务和大事务共同用一个事务,那么可以理解为子事务和大事务位于同一辆车上。如果子事务开启一个新事务,相当于子事务开了一辆新车,大事务和子事务位于不同的车上面

注意: 出现的异常回一层一层往上面进行传递,坐一辆车的全崩,开新车并且在异常之前执行的不崩;开新车,但是位于异常之后,崩。

注意:如果子事务出现了异常,并且子事务位于大事务的方法体内部,那么大事务会感受到异常,那么即便大事务和子事务开不同的车,大事务也会崩掉,因为方法体内部出现了异常

子事务只和上一级的事务坐一辆车,不会和上一级的上一级的事务坐一辆车,除非他的上一级的事务和他的上一级的上一级的事务坐一辆车

总结图



设置事务传播行为演示

@Repository


public class BookDao {


@Autowired


private JdbcTemplate jdbcTemplate;


/减去某个用户的剩余金额/


@Transactional(propagation = Propagation.REQUIRES_NEW)


public void updateBalance(String username,int price)


{


String sql="update account set money=money-? where name=?";


jdbcTemplate.update(sql,price,username);


}


/获取某本图书的价格/


public int getBookPrice(String isbn)


{


String sql="select price from book where ISBN=?";


return jdbcTemplate.queryForObject(sql, Integer.class,isbn);


}


/减去某本书库存/


@Transactional(propagation = Propagation.REQUIRES_NEW)


public void updateStock(String isbn)


{


String sql="update book_stock set stock=stock-1 where isbn=?";


jdbcTemplate.update(sql,isbn);


}


}


@Transactional(propagation = Propagation.REQUIRED)


//文件异常回滚


public void checkOut(String username,String isbn)


{


//1.减去库存


bookDao.updateStock(isbn);


//2.获取图书的价格


int bookPrice = bookDao.getBookPrice(isbn);


//3.减去余额


bookDao.updateBalance(username,bookPrice);


}




重点:REQUIRED 事务属性来源于大事务(子事务和大事务坐一辆车时),即子事务的所有属性,例如超时设置,回滚设置,都继承于大事务,即使子事务里面设置了,也没有用

propagation = Propagation.REQUIRES_NEW 可以调整,默认是 REQUIRED


REQUIRED 将之前事务使用的 connection 传递给这个事务使用

REQUIRED_NEW 这个方法直接使用新的 connection


本类事务方法之间的调用就只是一个事务

@Transactional


@Service


public class BookService {


@Autowired


BookDao bookDao;


@Transactional(propagation = Propagation.REQUIRES_NEW)


public void checkOut(String username,String isbn)


{


//1.减去库存


bookDao.updateStock(isbn);


//2.获取图书的价格


int bookPrice = bookDao.getBookPrice(isbn);


//3.减去余额


bookDao.updateBalance(username,bookPrice);


}


@Transactional(propagation = Propagation.REQUIRES_NEW)


public void updateStock(String isbn)


{


bookDao.updateStock(isbn);


}


@Transactional


void testmain()


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


{


checkOut("大忽悠","ISBN_001");


updateStock("ISBN_002");


System.out.println("结账成功");


int i=10/0;


}


}


主类:


public static void main(String[] args) {


ApplicationContext ioc= new ClassPathXmlApplicationContext("appOfDao.xml");


BookService bs = ioc.getBean(BookService.class);


//虽然 testmain 里面的两个方法都开了新车,


//但是 testmain 方法最后出现了异常,效果并不如预期般改变(即回滚了事务)


bs.testmain();


}


原因:代理对象调用方法的时候,才能实现事务的控制




无法进行事务控制,也就相当于无法通过动态代理,对方法进行增强的操作,无法进行增强的操作,当然也就无法进行事务控制了



在本类中给本类对象进行注入,会造成死循环

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
Spring入门后半部分----JDBCTemplate和事务控制