开发者学堂课程【高校精品课-上海交通大学-企业级应用体系架构:Transaction 1 】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/75/detail/15831
Transaction 1
内容介绍:
一、Transactions in Java EE Applications
二、What Is a Transaction?
三、Container-Managed Transactions
四、Transaction Timeouts
五、Bean-Managed Transactions
一、Transactions in Java EE Applications
What Is a Transaction?
Container-Managed Transactions
Bean-Managed Transactions
Transaction Timeouts
Isolation and Database Locking
Updating Multiple Databases
在 Java 程序中如何管理事务,管理事务是一种声明式,也就是想告诉容器,也就是像 Transactions 服务器应用配置的容器,如何管理,还有一种是硬编码,前一种方式会更实用。Container-Managed Transactions 和 Bean-Managed Transactions 是事务的边界,就是从什么时候开始到什么时候结束,多个事务并行执行的时候,数据库是怎么做隔离的,彼此之间不会因为并行的去操作,数据库导致数据混乱,当要操作多个数据库的时候,它是怎么实现事务管理,就是所谓的分布式事务是怎么来实现的。
1、In a Java EE application, a transaction
is a series of actions that must all complete successfully, or else all the changes in each action are backed out. Transactions end in either a commit or a rollback.
2、The Java Transaction API (JTA) allows applications
to access transactions in a manner that is independent of specific implementations
3、The JTA defines the UserTransaction interface that
applications use to start, commit, or roll back transactions.
Application components get a UserTransaction object through a JNDI lookup by using the name java : comp/UserTransaction or by requesting injection of a UserTransaction object.
java 的事务管理体系里面,提供 jta 的规范,有各种各样的实现,比如 java 自身,它会有相应的包去实践它,jboss提供事务的实现,如果用 spring 可以看到 spring 里面也有事务的实现,jta 的作用就是在把各家不同的事务的实现规范化,对着统一的 api 进行编程就可以对事务进行管理,如果要编程管理事务的边界,就要用到 UserTransaction 的接口,接口也在 jndi 上,在应用服务器里面绑定某位置上,按位置查找就能找到这一个接口的实践类,比如 spring 的实现类,当 spring 启动的时候就可以找到它,对它进行管理,这是 java 中提供的规范。
二、What Is a Transaction?
1、Pseudocode:
begin transaction
debit checking account
credit savings account
Update history log
commit transaction
2、A transaction is often defined as an indivisible unit of work
Either all or none of the three steps must complete.
3、A transaction can end in two ways:
with a commit or with a rollback.
事务在数据库里面,当对数据库进行操作的时候,事务的意思是
操作可能包含多步,步骤在某地方确定,开始事务,到某个地方提交事务,有可能事务失败需要回滚,从begin到credit 中间的操作被定义成事务,特点是要么都做,要么都不做,如果事务执行第一步成功,第二步成功,第三步失败,会把前两步成功的动作进行回滚,也就是把它恢复成在 begin 之前的状态,一二三都执行,都不执行,事情本身比较简单。要看是不是只有数据库支持动作,jms,消息中间件,消息属性里面有 groupid,表示消息属于哪一组,在这一组里面的id,它关联的下一组的 id 是什么,这一组消息要么都接收,要么都不接收,消息中间件里面有邮箱,邮箱会把消息写到硬盘上,quenu 会把消息写到硬盘上,当用户读消息时候,这一组消息有五条,把五条都读走,消息就算是被读走,整个被读走,从硬盘上删除,如果只读走两个,在读第三个的时候消息还没有读成功,那么会尝试把三的消息再转发,如果还是不成功,会把一二消息也标记成未接收,会让接收者要么五条都接收,要么五条都不接收,jms 连接工厂,本身像数据库一样可以实现群发群收,也就是一组操作要么都执行,要么都不执行,所以事务不只是发生在数据库上,理论上发生在 resourece manage 资源管理器上,资源管理器,数据库是一种,交易中间件是一种,邮件服务器,邮件的群发和群收,一样的道理,也是事务可以支持资源管理器,只要是资源管理器,都有事务的能力,都可以做群发群收类似的,要么都做,要么都不做,所以一定要注意事务管理不是只有数据库,还有其它东西,至少应该知道有消息服务器和邮件服务器,都属于事务性,资源管理器,都支持事务的资源管理器,利用内存,它有很大的缓存,当执行事务的时候,会把事务的结果写到缓存里,当提交的时候,就要看一下当前在内存里的事情能不能提交,真正写入到数据库或者消息中间件里面,就是把它状态更新掉,也就是把硬盘里面实际的状态关掉,如果可以就做,如果不行,比如有些操作没有执行化,没有明确的告诉事务要提交,不管什么原因就是因为整个事务没有成功,只要把内存里缓存的中间状态删掉就可以,数据库里或者在消息中间的硬盘上,最终存储的还是在事务执行之前的状态,只要靠内存就可以,所以没有什么特别神奇的地方,为什么能管理事务,就是靠大量的内存做缓存实现,引申出附加的问题,tomcat,对 oracle,定义的事务是 tomcat 里有变量 A,注意事务从 commit 开始 a++,第二步把 oracle 的数据库,比如里面的某一个账户钱减少一点,第三步把另外账户加一点,做转账操作,a是程序里在记录到底执行过多少次转账操作,把事务提交掉,第一步成功,第二步成功,第三步失败,其中第一步和第二步发生在 oracle 的数据库里,失败直接释放缓存,数据库里存的仍然是原来的钱,a能回来吗,a的状态要自己去维护,二三发生在数据库里,a 是在 tomcat 里,自己的程序里,数据库没有办法直接把a恢复到a++之前的状态,要靠自己,引申出问题,当事务发生要让它回滚的时候可以告诉事务的资源管理器要回滚,无论是数据操作放弃掉还是消息全部接收掉,都是受事务型资源管理器控制的,它可以做但是如果在事务当中涉及到对自己程序管理里面,是自己控制的变量的操作,要想回滚就必须要自己管理,靠数据库是回滚不了的,这是引申出来的第三个问题。
三、Container-Managed Transactions
In an enterprise bean with container-managed transaction
demarcation,
the EJB container sets the boundaries of the transactions.
事务需要有划分边界,事务有四个特性 acid,a 是原子的操作,如果涉及到多条,要么都执行,要么都不执行,不可能一部分执行,另外一部分没有执行,原子操作本身对应什么,要知道事务的边界从哪里开始,原子操作操作一二三,三个动作从什么时候开始到什么时候结束,中间的操作都定义成事务里面的原子操作,一致性,因为有连续性,三个动作,比如要把账户减一,第二动作是减二,三个动作是减三,是把 n 个动作全部执行完之后,如果能提交,就把它持久化到硬盘上,但是现在如果在数据约束上有要求,账户里面的余额必须要大于等于一,就是必须有一块钱,如果余额有十块钱,第一个动作要减五块,第二个动作减五块,第三个动作减五块,如果定义在一个事务里,先把它缓存,最后再提交到硬盘上,把三个全部都执行一遍,余额就会变成负,不允许发生,在数据上的约束,无论事务有多长,有多少条语句,最后执行完数据上的约束要一致,所以三个操作必然有一个要失败,大于等于零,所以减掉五还剩五,在减掉第二个五的时候,已经不能讲,所以一致性是不管在事务里执行多少个操作,中间的状态,怎么去变化,最终提交的要满足数据约束,不可能因为在缓存,然后一次性提交,变成负的,隔离性是如果要做转账操作,现在银行里的余额是十块钱,A来执行转账操作,给余额加五块,b也做转账操作,要加五块,那么加完之后余额是20,各转五块,但现在如果不做事务的隔离就会变成想转账的时候,读余额是十块,因为它俩并行操作,把钱加五块,那就变成15,b加五块也是15,a写回来写个10,ab执行两次转账操作,但实际上最后数据库里出现的15块,这就是因为事务之间没有隔离,所以隔离在病发的时候怎么让事务互相之间,不要干扰,如果是执行两个,就要把两个效果都能体现出来,B是持久性,在缓存里执行,当提交时,存储到硬盘上,不会因为事务的原因,事务最后提交,但是隔了一会发现钱又回到十块,不会出现这种状态,最重要的就是要划分事务边界,唯一能控制的,事务的一致性是靠资源管理器,也就是数据库保证不会允许在数据上已经加过限制,余额大于一的情况下余额负,隔离性是告诉数据库,希望如何隔离,持久性是数据库自带的一种属性,当写进去之后,它一定会写入到硬盘上,无论是数据库还是消息中间件还是其它的东西,自己可以带出去,四个合到一起,事务的属性,a 和 i 是可以控制的,控制事务的边界,这就所谓的事务边界去划分原子性操作的意义,现在写两个代码,如果在第二个 bean 里面有方法叫做B X,在定义里面有X叫做 method-a,method-a 在执行操作的时候,系统开了一个事务是tx1,
method-A() {
bean-2. method-B ( )
}
整个事务是 tx1,整个 method-A 在 tx1里面去执行,调用 method-b,method-b在 tx1执行,加入 tx1,是tx1的一部分,声明式的事务属性管理会告诉未必,允许更灵活的方式来执行。可以让 method-b不一定非要在 tx1里,还可以其它的选择。
四、Transaction Timeouts
1、NotSupported
Invoking a method on an EJB with this transaction attribute suspends the transaction until the method is completed.
Transactional 里面可以写属性,属性有六种,不是spring自己定义的,是 java 事务 api 里面定义的,六种不同的属性决定方法 b 是不是在 tx1里执行,还是在其它的事务执行,按照字母排序,并不是按常用性排序,NotSupported每个方法都会有事务属性,先忽略 method a,因为已经开个tx1,在方法前面加 Transactional,属性是什么,如果是NotSupported,执行到这里,方法不支持事务,所以把事务挂起,在非事务的状态下执行当前的方法,执行完之后再把原来的挂起,继续执行,现在在tx1里面在执行事务,执行,因为是 NotSupported,所以会把tx1挂起,然后method-b就会在没有任何事务的情况执行,执行完之后把tx1恢复出来,所以在这种情况下 method-a 里面一直到调用b之前的代码以及调用完b之后的代码都属于事务,但是B是在非事务的状态下去执行的,它既没有加入到 tx1里,也本身没有在任何处理执行,所以如果tx1要回滚,回到tx1所有没操作的状态,但是B本身的操作不会被忽略,而因为B是在没有任何事务状态下执行,所以B如果失败不影响tx1,B如果失败,执行操作失败,b记日志失败,如果tx1其它操作都成功,tx1的操作还是会被提交,b不会加入到tx1里面,
假如操作是在做转账操作,把钱从a账户里面扣掉,加到b的账户里面,存进去,在中间想做记日志的操作,到本地的硬盘上写 log 文件,发现本地的硬盘使用年限比较久,它的性能不是很好,容易出错,转账本身是成功的,只是记日志不成功,要不要让整个事务回滚,如果从提高用户可用性的角度,不要回滚,在这种情况下,日志能记就记,日志记不记成功对于整个事务要不要提交不产生任何影响,但是日志必须在这记,也就是a只要把钱取出来,必须记日志,只是是否记成功都无所谓,能记就记,把 method-b设置成 NotSupported
,它不影响 tx1的提交,tx1成功与否与 B 成功与否没有任何关系,而且B本身它在非事务状态下执行,所以它也不存在回滚,不会因为记不上问题,还要把已经记的抹掉。
2、Supports
This attribute means that the enterprise bean method will be included in the transaction scope if it is invoked within a transaction.
把 a 的钱扔掉,取出来,调用 B,把钱加到 B 的上面,代码仍然这么么写,功能也是一样,它在记日志,如果转账操作成功,日志就一定要记成功,只要有事务,成功就一定要记成功,如果失败就不记了,但是如果不管要不要回滚,在没有事务的状态下去执行转账操作,钱已经被扣,但是存进去之后失败了,也不需要回滚,认为这件事无所谓,是否人工调就行,b在没有事务的状态下去执行,成功就成功,失败就失败,不是失败就不记,如果条件这样,那么事务属性就是 supports,作用就是如果定义tx1,那 B 就是成为tx1的一部分,如果转账,记日志失败回去,因为转账成功,一定要把日志记上,如果日志记不上,将来对账对不上,要把转账操作恢复回去,所以B就会成为tx1一部分,如果在某一次操作的时候没有开任何的事务,直接调用A,认为这是非关键的操作,在非事务的状态下执行,成功就成功,失败就失败,不考虑回稳或者提交的动作,用的是supports,代码本身没有发生任何变化,只是在 Transactional里换属性而已。如果有事务就加入事务,如果没事务,就在非事务状态下,不会拍事务,一定要在事务下记日志。
3、Required
This attribute means that the enterprise bean method must be invoked within the
scope of a transaction.
如果之前的代码没有加Transaction是因为不加默认值就是 required,一定要在事务里执行,上半部跟 supports是一样的,如果操作在 tx1执行,B 会加入到 tx1里,它会成为 tx1的一部分,如果操作没有失误,B 在 require 模式里面,B 一定会自己先开事务,在事务里执行自己的逻辑,小 a 和小 B 代码会合到一起执行,在非事务的状态下执行,如果日志记错,会回滚,但是回滚不影响a和b,a 和 b本身不存在回滚和提交,执行完直接持久化,问题能得到简化,a 方法有属性,在执行第一条代码之后就开启事务 tx1,当时第二条,第三条,第四条,第五条是调用 require 的时候,把 B 自动加入到 tx1继续执行,可以保证所有的方法在事务里执行,但是不跟事务合成一个事务,AB 操作,然后 b,无论如何新事务执行,都不会加入到 a 和 B 里面,所以 B 会在新开的 tx2的事务执行,ab 会合成事务或者在非事务状态下执行,不管哪一种执行完,B 操作记日志操作一定在单独的日志里面操作,比如日志一定要写成功,要写三条,谁登陆进来,a减多少钱,加到 B,日志操作在写文件或者写数据库,反正是写日志,写日志操作必须要么都写成,要么都不写,但无论怎么样,成功与否不影响转账这件事情,转账可以回滚,或者提交,写日志,互相之间不干扰。
4、Mandatory
This attribute means that the enterprise bean method must always be made part of the transaction scope of the calling client.
强制要求必须有事务,否则报错,如果有事务加上事务,如果没有就报错。
5、Never
This attribute means that the enterprise bean method must not be invoked within the scope of a transaction.
强制没有事务,在非事务状态下执行,就加入非事务状态,如果有事务直接报错,代码都是一样的,执行完小a执行大B,执行小b代码没有做任何修改,但是赋予a和b不同的事务属性后,就会有不同的效果
Transaction Attribute |
Client's Transaction |
Business Method's Transaction |
Required |
=【】 |
T2 |
Required |
T1 |
T1 |
RequiresNew |
None |
T2 |
RequiresNew |
T1 |
T2 |
Mandatory |
None |
Error |
Mandatory |
T1 |
T1 |
NotSupported |
None |
None |
NotSupported |
T1 |
None |
Supports |
None |
None |
Supports |
T1 |
T1 |
Never |
None |
None |
Never |
T1 |
Error |
转账操作由两个方法构成的,wichdow,deposit,Transfer 和 wichdow 合到一起,处境一样,Transfer上面没有任何事务,deposit 会在什么情况下执行,tomcat 获取其它事务管理器的逻辑,它执行到 Transfer 事务属性是什么,none,要在非事务的状态下进行执行,required 必须在事务下执行,所以现在当前没有事务,所以Transfer会新开一个事务,在T1下面执行,Transfer,wichdow,deposit,假设全是 Require,过来之后,现在没有事务,Transfer新开一个事务,事务是t1,进去执行 wichdow,当前执行线程上已经有t1事务,Require 必须在事务里执行,如果有加入进去,因为现在线上已经有新事务,踢掉,加入到里面,当 wichdow 执行完的时候,每个方法上面都写事务属性,所以每个方法结束完之后都会去检查一下,方法只要 return 就检查一下,required 事务是该提交还是回滚,wichdow是执行完,T1是加入进来的,不是在这里开的,所以 tomcat 不会提交或者回滚,它继续往下执行,执行到deposit,deposit 同样的道理,会加入到t1,执行完之后,每当执行一个方法,从方法返回的时候,tomcat都会做检查,在deposit 执行完之后 Tomcat 看再做一次检查,发现T1事务不是 deposit 开的,从这里 begin,所以也不做提交动作,transfer 执行完之后,再次检查发现T1事务就是 transfer 开的,所以现在都正常执行完就提交掉,在提交掉之后T1结束,于是 wichdow 和 deposit 转账操作都在 T1事务里执行,都是 required,假设 wichdow 和 transfer都是required,deposit 是 requiresdnew,transfer 开t1,t1执行到 wichdow,wichdow 也是 required,加入到t1里面,wichdow 执行完之后,尝试提交事务发现提交不了,因为t1不是它开的,所以继续往下执行,执行到 deposit时按照 requiresdnew 逻辑,当前如果有事务,就把事务挂起,然后在新事务里执行当前的方法,执行完之后,把事务恢复,变成把t1挂起,新开一个事务比如t2,当deposit结束,就要看t2要不要提交,t2确实在 deposit 地方,所以t2被提交掉,t1被恢复出来,再尝试t1要不要提交,发现T1不是 deposit 开启,所以T1没有被提交,transfer 结束,再尝试t1要不要提交,因为现在线程关联的是 t1,发现 t1确实是 transfer 开启的,所以T1就被提交掉,Transfer 和wichdow在T1里,deposit在 T2里,如果想回滚,假如t1回滚,操作回滚不了,T2想回滚,单独回滚,在执行自己的时候可以单独回滚,但是回滚不回滚都对 T1不造成影响,同样的代码,没做任何其它的修改,只改上面的属性标签,变成两个事务提交,按照不同的事务属性进行不同的设置的时候,tomcat 会实现事务边界的不同的划分,可能在一个事务里,可能在两个事务里,也可能直接报错,代码没有做任何修改,靠 error,session 不同的值告诉 tomcat 怎么管理事务,声明式的划分事务边界,好处是比较简单,代码不用做大量的修改,缺点是力度,到方法几,wichdow 里面还有三个动作,进一步做控制,做不到,除非把三个动作再封装成不同的方法,这种方式会比较简单,声明式也叫容器管理,就是完全靠tomcat 管理,没有明显的通过自己的代码在哪里开始到哪里结束。
Transaction attributes are specified by
decorating the enterprise bean class or method with
a javax. ejb. TransactionAttribute annotation
and setting it to one of the javax. ejb. TransactionAttributeType constants.
Transaction Attribute |
TransactionAttributeType Constant |
Required |
TransactionAttribute Type.REQUIRED |
RequiresNew |
TransactionAttributeType.REQUIRES_ NEW |
Mandatory |
TransactionAttribute Type.MANDATORY |
NotSupported |
TransactionAttribute Type.NOT_ SUPPORTED |
Supports |
TransactionAttribute Type.SUPPORTS |
Never |
TransactionAttribute Type.NEVER |
六个事务属性对应的就是看到全程,不是 spring 的代码,是 java 包里面定义的东西。
The following code snippet demonstrates how to use
the @Trans actionAttribute annotation:
@TransactionAttribute (NOT_ SUPPORTED)
@Stateful
public class TransactionBean implements Transaction {
@TransactionAttribute(REQUIRES_ NEW)
public void firstMethod() {...}
@TransactionAttribute ( REQUIRED)
public void secondMethod() {...}
public void thirdMethod() {...}
public void fourthMethod() {...}
}
有的是用 spring,名字叫 transaction,内容写里面例子类似的类,接口里面的名字,六种当中的一种,在定义的时候,对于类,可以定义类里所有的方法都用属性,如果有些方法特殊,可以在方法上面特别的重载一下,声明式的事务边界。
Rolling Back a CMT
There are two ways to roll back a container-managed
transaction.
First, if a system exception is thrown, the container will automatically roll back the transaction.
Second, by invoking the setRollbackOnly method of
the EJBContext interface, the bean method instructs the container to
roll back the transaction.
If the bean throws an application exception, the rollback is not automatic
but can be initiated by a call to setRollbackOnly.
有时候可以提交,但有时候要回滚,当碰到某一个事务,在执行的过程中,可能碰到某种异常,应该把事务设置成只能回滚,事务不能被正常提交,在自己的代码里做 a++动作,再去执行数据库操作,整个定义成事务,后面一部分可以靠数据库在缓存里面搞定,如果想要让容器做这样的动作,服务类实现接口,接口里有三个方法,afterbegin事务开始之后,用暂存的变量去存当前变量的值,afterCompletion 事务完成之后,参数表示当前事务是,如果失败了,就把值恢复成缓存的时候,把自己的程序里面维护的变量加入到事务以后也能够有回滚的效果,在spring里面的实现不一样,但是基本道理是一样,在 java 企业版里面讲的标准的实验方法,事务的实现不复杂,就是数据库里面开了一块很大的缓存,所有的操作都在缓存里,当回滚的时候就把缓存里释放掉,当提交的时候写入到数据库里,让其它人能看到就可以,对于自己程序里面的一些变量的处理,数据库管不到,tomcat 会主动帮管,需要自己处理,无论是tomcat 还是 deposit 应用服务器,也不知道定义的哪些变量希望回滚,所以只开放接口,让自己去想要不要回滚。
五、Bean-Managed Transactions
In bean-managed transaction demarcation,
the code in the session or message-driven bean explicitly marks
the boundaries of the transaction.
Pseudocode:
begin transaction
update table-a
if ( condition-x)
commit transaction
else if (condition-y)
update table-b
commit transaction
else rollback transaction
begin transaction
update table-c
commit transaction
如果想做更细力度的控制怎么办,自己去定义什么时候开始 transaction 什么时候提交,每次都是在session上面定义transaction,编程式的实现,就是自己实现,自己人为的实现事务边界的划分,不推荐这种方式,因为这种方式写代码写的很烦,而且不灵活,就以数据库为例,要开大量缓存,把执行的中间结果都写在这里,而且事务在进行操作的时候,通常在数据库里面,如果是一张表,可能在上面要加锁,锁占资源,锁本身是个资源,事务不能让它无休止的执行下去,如果无休止执行下去,系统的性能会非常差,所以在事务上可以设置 timeout,强制要求在程序里面执行的事务在执行多长时间之后就一定要结束,至于 timeout 之后是让它回滚还是提交,但是必须要设计timeout,防止有些程序恶意的占用数据库的锁,事务只能在给定时间内执行,超过就全给剥夺掉,访问数据库表,表里面数据量很大,执行的时间会非常长,设置超时,总是超时,超时要根据估算去设置,不能随便设置,执行时间很长的事务叫做长事务,控制逻辑不是现在这样,不能加锁,不能直接在这里回滚,或者提交,
要用一种事后补偿的机制去执行,直接写进数据库,如果失败,会标记事务失败,标记是否要执行一次补偿机制,这是应对长事务的手段,尽量避免长事务,如果真碰到长事务不要靠加锁的机制去实现,甚至不要定义事务,应该事后去做补偿,事后去做补偿,减少加锁的时间,尽量减少占内存的空间,只是事后发现执行不正常,到某一步不正常的时候,想办法做补偿,所以这是在设计的时候要去考虑的问。
Managing Transactions in Spring
@Transactional propagation
事务传播行为 |
说明 |
@Transactional(propagation=Propagation.REQUIRED) |
如果有事务,那么加入事务,没有的话新建一个(默认情况) |
@Transactional(propagation=Propagation.NOT_ SUPPORTED) |
容器不为这个方法开启事务 |
@Transactional(propagation= Propagation.REQUIRES_ NEW) |
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务 |
@Transactional(propagation=Propagation.MANDATORY) |
必须在一个已有的事务中执行,否则抛出异常 |
@Transactional(propagation=Propagation.NEVER) |
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反) |
@Transactional(propagation=Propagation.SUPPORTS) |
如果其他bean调用这个方法,在其他bean中声明事务,那就 用事务。如果其他bean没有声明事务,那就不用事务 |
事务传播属性,transfer 在t1里,wichdow 在t1里面,transfer 把事务传播给 wichdow,wichdow传播给deposit一样,表达在 spring 里面,只是标签不一样,写法不一样,意思都是一模一样,在执行的时候,之前没有做任何的设计,是因为不需要定义的时候,
默认的就是用的 require,也可以去写,不写默认是 require,在 book service 里面执行方法,require类型的生命词事务,执行底下 update 操作的时候,一定会在事务里执行,这件事情要么成功,要么回滚,不会插入到一半的时候,两个属性进去,第三个属性没进去,失败,写进去不完整的记录,不会出现这种情况,spring的操作。
BookingService.java
@Component
public class BookingService
{
private final static Logger logger = LoggerFactory.getLogger(BookingService.clas);
private final JdbcTemplate jdbcTemplate;
public BookingService (JdbcTemplate jdbcTemplate)
{
this.jdbcTemplate = jdbcTemplate;
}
@Transa
c
tional
public void book (String... persons)
{
for (String person : persons)
{
logger.info("Booking " + person+ " in a set...");
jdbcTemplate.update("insert into BOOKINGS(FIRST_ NAME) values (?)", person);
}
}
public List<String> findAllBookings()
{
return jdbcTemplate.query("select FIRST_ NAME from BOOKINGS",
(rs, rowNum) -> rs.getString("FIRST_NAME"));
}
}