以下内容集成到了GitHub项目中,有兴趣的朋友可以看一下GitHub
https://github.com/HeShuai-GitHub/springDemo.git
随手记
权限:read 1,write 2,execute 4
一、spring maven依赖引入
<!--spring依赖 start-->
<!--这个jar文件包含Spring 框架基本的核心工具类。Spring 其它组件要都要使用到这个包里的类,是其它组件的基本核心-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)
操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 这个jar 文件为Spring 核心提供了大量扩展。可以找到使用Spring ApplicationContext特性时所需的全部类,
JDNI 所需的全部类,instrumentation组件以及校验Validation 方面的相关类。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--支持缓存Cache(ehcache)、JCA、JMX、 邮件服务(Java Mail、COS Mail)、任务计划Scheduling(Timer、Quartz)方面的类。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--模块提供了一个强大的表达式语言,用于在运行时查询和处理对象图。该语言支持设置和获取属性值;属性赋值,
方法调用,访问数组的内容,收集和索引器,逻辑和算术运算,命名变量,并从Spring的IOC容器的名字对象检索,
它也支持列表选择和投影以及常见的列表聚合。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--spring jdbc依赖,这个jar 文件包含对Spring 对JDBC 数据访问进行封装的所有类。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--依赖数据库的jar包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>
<!--AOP引用 start-->
<!--这个jar 文件包含在应用中使用Spring 的AOP 特性时所需的类和源码级元数据支持。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
<!--提供对AspectJ的支持,以便可以方便的将面向方面的功能集成进IDE中-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<!--AOP引用 end-->
<!--spring依赖 end-->
二、jdbcTemplate
1.介绍
jdbcTemplate是spring为了更加易于使用JDBC操作数据库而封装的一个抽象层,以此方便对数据库的增删改查
作为spring的核心,jdbcTemplate的设计之初,是为了不同jdbc的操作提供模板方法,通过这种方式,可以在尽可能灵活的情况下,将数据库的存取工作降到最低。
以下将创建一个简单的jdbcTemplate 增删改查的例子,这里为了减少代码冗余,使用了Junit单元测试,对单元测试不了解的朋友可以直接参考BenHeart
的博客
2. 例子
2.1 所涉及到的mysql 数据库表
CREATE TABLE wct_city (id int NOT NULL AUTO_INCREMENT, title varchar(500), ownid varchar(10), sid int, shop varchar(255), PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO wct_city (id, title, ownid, sid, shop) VALUES (1, '秦皇岛', null, null, null);
INSERT INTO wct_city (id, title, ownid, sid, shop) VALUES (2, '石家庄', null, null, null);
2.2 数据库表所对应的modal类
public class CityModal {
private String id;
private String title;
private String sid;
private String shop;
@Override
public String toString() {
return "CityModal{" +
"id='" + id + '\'' +
", title='" + title + '\'' +
", sid='" + sid + '\'' +
", shop='" + shop + '\'' +
'}';
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getShop() {
return shop;
}
public void setShop(String shop) {
this.shop = shop;
}
}
2.3 junit测试实现类
public class CityDaoTest {
private JdbcTemplate jdbcTemplate;
@Before
public void before() throws Exception {
ApplicationContext ap=new ClassPathXmlApplicationContext("jdbc/JDBC-Context.xml");
jdbcTemplate=ap.getBean("jdbcTemplate",JdbcTemplate.class);
}
@After
public void after() throws Exception {
}
/**
* 测试JdbcTemplate的query查询方法,queryForObject返回值是一个泛型,较适合查询一条语句
* query 返回类型为list<T>,返回的是一个数组,所以比较适合返回一组数据
*/
@Test
public void query(){
String sql="select id,title,ownid,sid,shop from wct_city ";
RowMapper<CityModal> rowMapper=new BeanPropertyRowMapper<CityModal>(CityModal.class);
List<CityModal> list=jdbcTemplate.query(sql,rowMapper);
for(CityModal city:list){
System.out.println(city);
}
System.out.println("*************************");
sql="select id,title,ownid,sid,shop from wct_city where id= ?";
CityModal cityModal=jdbcTemplate.queryForObject(sql,new Object[]{1},rowMapper);
System.out.println(cityModal);
}
/**
* jdbcTemplate.update方法,可以对数据库做增删改操作
* jdbcTemplate的编码格式可能和数据库的编码格式出现不一致的情况,
* 所以需要在数据连接的url后面添加“characterEncoding=utf8”
* 如:jdbc:mysql://localhost:3306/usrdel?characterEncoding=utf8
*/
@Test
public void update(){
String sql="update wct_city set title= ? where id= ?";
jdbcTemplate.update(sql,"秦皇岛","1");
}
/**
* jdbcTemplate.update 对数据库进行添加操作
*/
@Test
public void insert(){
String sql="insert wct_city values(null,?,?,?,?)";
jdbcTemplate.update(sql,"唐山","1","2","3");
}
/**
* jdbcTemplate.update 对数据库进行删除操作
*/
@Test
public void delete(){
String sql="delete from wct_city where title=?";
jdbcTemplate.update(sql,"唐山");
}
}
三、事务管理
1. 事务简介
事务具有四种特性ACID:原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)
,相关的概念,有很多博客都有介绍,在这就不一一赘述了,推荐一个博客,有兴趣的可以看一下,王王王王王景的博客
2. 直接上例子
2.1 数据库表
DROP TABLE test_book;
CREATE TABLE test_book (id int NOT NULL AUTO_INCREMENT, rmb decimal(11,0) DEFAULT '0' COMMENT '书价', count int DEFAULT '0' COMMENT '书的库存', PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO test_book (id, rmb, count) VALUES (1, 30, 10);
DROP TABLE test_user;
CREATE TABLE test_user (id int NOT NULL AUTO_INCREMENT, username varchar(50) COMMENT '用户名', rmb decimal(11,0) DEFAULT '0' COMMENT '账户余额', PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO test_user (id, username, rmb) VALUES (1, '小明', 50);
2.2 Dao层设计
/**
* book表Dao层接口
*/
public interface BookDao {
Book query(Integer id);
void update(Integer id,Integer count);
}
/**
* User表Dao层接口
*/
public interface UserDao {
User query(Integer id);
void update(Integer id,Double rmb);
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate dao;
public User query(Integer id) {
String sql="select id,username as userName,rmb from test_user where id= ?";
RowMapper<User> row=new BeanPropertyRowMapper(User.class);
User user=dao.queryForObject(sql,new Object[]{id},row);
return user;
}
public void update(Integer id, Double rmb) {
User user=this.query(id);
String sql="update test_user set rmb= ? where id = ?";
dao.update(sql,user.getRmb()-rmb,id);
}
}
@Repository
public class BookDaoImpl implements BookDao {
@Autowired
private JdbcTemplate dao;
public Book query(Integer id) {
String sql="select id,rmb,count from test_book where id= ?";
RowMapper<Book> row=new BeanPropertyRowMapper(Book.class);
Book book=dao.queryForObject(sql,new Object[]{id},row);
return book;
}
public void update(Integer id, Integer count) {
Book book=this.query(id);
String sql="update test_book set count= ? where id = ?";
dao.update(sql,book.getCount()-count,id);
}
}
2.3 service层设计
public interface BuyTwoBooksService {
void buyTwoBooks();
}
public interface BuyBookService {
void buyBook();
}
@Service
public class BuyBookServiceImpl implements BuyBookService {
@Autowired
private BookDao bookDao;
@Autowired
private UserDao userDao;
/**
* 购买书籍,若不设置事务处理,会产生什么效果呢,可以将@Transaction注解注释后,看一下效果,会造成
* book表中的库存含量减少了一本书,而user表中的用户余额因为不足,而导致扣款失败,这样就影响了事务中的一致性
*
*@Transactional 标注该方法整体为一个事务,需要满足事务的原子性,该标签底层使用aop的思想,也就是动态代理,
* 所以在spring ioc容器中管理的实际上是代理类,该注解可以在方法上,可以在类上,如果在类上表示类中所有方法都需要满足事务的一致性
* @Transactional 的参数介绍
* propagation: A方法和B方法同时具备事务,A方法调用B方法时,会将A方法的事务传递给B方法,B方法对于事务的处理方式就是事务的传播性(propagation)
* Propagation.REQUIRED:默认该值,即当A方法调用B方法时,当A方法具备事务,则B方法不会创建事务,否则B方法创建一个新的事务,在自己的事务中运行(常用)
*以下不举例A、B两个方法了,传播性就是多个事务之间的交互
* Propagation.REQUIRES_NEW:当前的方法必须启动新事物,并在他的事务内运行,如果有事务正在运行,则将它挂起(常用)
* Propagation.SUPPORTS:如果有事务正在运行,则当前方法就在事务中运行,否则当前方法不在事务中运行
* Propagation.NOT_SUPPORTED:当前方法不应该运行在事务中,如果有运行的事务,则将它挂起
* Propagation.MANDATORY:当前的方法必须运行在事务内部,如果没有事务,则抛出异常
* Propagation.NEVER:当前的方法不应该运行在事务中,如果有运行的事务,则抛出异常
* Propagation.NESTED:如果有运行的事务,当前的方法应该在运行的这个事务的嵌套事务中运行,如果没有,就启动一个新事物,并在自己的事务中运行
*
* isolation: 事务的隔离级别,这个就不解释了,网上有太多的介绍,也属于关系型数据库基础
* Isolation.DEFAULT:根据数据库的默认级别来进行设置
* Isolation.READ_UNCOMMITTED:读未提交
* Isolation.READ_COMMITTED:读已提交
* Isolation.REPEATABLE_READ:可重复读
* Isolation.SERIALIZABLE: 串行化
* timeout: 设置事务超时时间,当事务超时后强制进行回滚操作
*
* readOnly: 指定当前事务中的一系列操作是否为只读,并非强制当前方法中只可以包括读操作
* readOnly最主要的作用是通知mysql不加锁,以提高数据库的性能
* 如果readOnly设置为True,但是当前方法中不只包含读操作,还有写操作,在不加锁的情况下,将造成赃读、不可重复读、幻读等问题(需谨慎使用)
*
* rollbackFor||rollbackForClassName||noRollbackFor||noRollbackForClassName:设置事务回滚条件,不设置默认发生任何异常都进行回滚
* rollbackFor:只有设置的异常才会回滚
* noRollbackFor:设置的异常不进行回滚,其他都进行回滚
* rollbackForClassName和noRollbackForClassName:基本上上面一致,不过一个是对象参数,一个是类全限定名参数
*/
@Transactional(rollbackForClassName = {"Exception"})
public void buyBook() {
System.out.println("开始购买书籍");
Book book=bookDao.query(1);
System.out.println("书籍价格为:"+book.getRmb()+",书籍库存:"+book.getCount());
//对编号为1的书籍,购买一本
bookDao.update(1,1);
book=bookDao.query(1);
User user=userDao.query(1);
System.out.println("购买书籍的人是:"+user.getUserName()+",账户余额:"+user.getRmb());
if(user.getRmb()<book.getRmb()){
throw new RuntimeException("用户账户余额不足...\n"+"现在书籍库存是:"+book.getCount()+",账户余额:"+user.getRmb()
+",\n如果标记了@Transactional,他将在发生异常后发生回滚,数据库数据不变");
}
userDao.update(1,book.getRmb());
user=userDao.query(1);
System.out.println("购买书籍结束");
System.out.println("现在书籍库存是:"+book.getCount()+",账户余额:"+user.getRmb());
}
}
/**
* @program: springDemo
* @description: 购买两本书,以此来测试事务的传播性,也就是@Transactional的Propagation属性
* @author: hs
* @create: 2020-08-16 20:39
**/
@Transactional //对该类中的所有方法起作用
@Service
public class BuyTwoBooksServiceImpl implements BuyTwoBooksService {
@Autowired
private BuyBookService buyBookService;
public void buyTwoBooks() {
System.out.println("购买两本书 开始");
System.out.println("购买第一本书 开始");
buyBookService.buyBook();
System.out.println("购买第一本书 结束");
System.out.println("***************");
System.out.println("购买第二本书 开始");
buyBookService.buyBook();
System.out.println("购买第二本书 结束");
System.out.println("购买两本书 结束");
}
}
以上分别包含了两个dao接口,两个实现,两个service接口,两个实现,代码较多,有兴趣的同学可以查看我的GitHub
2.4 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:context="http://www.springframework.org/schema/context" 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-4.2.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"
>
<!--使用Bean定义 来读取properties文件-->
<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:/jdbc/db.properties"></property>
</bean>-->
<!--context命名空间 读取资源文件 读取properties文件
必须先配置context命名空间,xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"
-->
<context:property-placeholder location="jdbc/db.properties"></context:property-placeholder>
<!--获取连接数据看的数据源-->
<bean id="dataSource" 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.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--将数据源注入到jdbcTemplate中-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--设置事务管理器,成员属性dataSource用来设置数据源-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启事务管理注解方式,transaction-manager用来设置事务管理器-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
<!--扫描指定路径下的包,并开启spring注解模式-->
<context:component-scan base-package="com.spring.jdbc.transaction"></context:component-scan>
</beans>
2.5 Junit单元测试类
**
* BuyBookServiceImpl Tester.
* 注解配置事务管理方式的测试
*
* @author <Authors name>
* @since <pre>8�� 16, 2020</pre>
* @version 1.0
*/
public class BuyBookTest {
private BuyBookService buyBookService;
private BuyTwoBooksService buyTwoBooksService;
@Before
public void before() throws Exception {
ApplicationContext ap=new ClassPathXmlApplicationContext("jdbc/JDBC-Context.xml");
buyBookService=ap.getBean("buyBookServiceImpl", BuyBookService.class);
buyTwoBooksService=ap.getBean("buyTwoBooksServiceImpl",BuyTwoBooksService.class);
}
@After
public void after() throws Exception {
}
/**
*
* Method: buyBook()
*
*/
@Test
public void testBuyBook() throws Exception {
buyBookService.buyBook();
}
@Test
public void testBuyTwoBooks() throws Exception{
buyTwoBooksService.buyTwoBooks();
}
}
以上是事务管理以注解方式实现的全过程,以xml方式实现的方法就不粘贴过来了,内容太多了,有需要的还是去GitHub查看吧
四、结语
本章主要是学习了spring的JDBC模块,重点是事务管理方面,spring的事务管理它的实现也是依靠于它的Aop模块的支持,这个在事务管理Xml的配置方式中可以很直观的看出来,但是最方便的方式肯定是使用@Transaction的注解配置方式。
可能在学习的过程中,有些关于基础性的数据库事务内容,没有写全,导致这章博客质量较低,因为这些东西在网上有太多比我理解深刻的博客可以参考了。
自此,spring的学习应该是到一个阶段了,下一阶段系统学习mybatis
如果有大神路过,请不吝赐教