一、AOP注解(注解方式)
在Spring02中讲述了xml方式的aop,现在讲解aop的注解形式。
导入jar包
四个基本包
Spring包下:spring-aop-4.2.9.RELEASE.jar,spring-aspects-4.2.9.RELEASE.jar
相关jar包:aopalliance-1.0.jar,aspectjweaver-1.8.9.jar
(和aop的xml方式导入的jar包一样)导入约束
导入AOP的约束在xml中打开aop的自动代理开关,同时增强类和业务逻辑类也是必须的
如果是IOC的注解,采用context:component-scan来打开
如果是AOP的注解:采用aop:aspectj-autoproxy
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" ></bean>
<bean id="logger" class="com.itheima.util.Logger" ></bean>
<!-- 扫描开关打开有两种:
如果是IOC的注解, 采用context : conponent-scan 开关去打开
如果是AOP的注解 , 采用aop: aop:aspectj-autoproxy
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 在增强类中打上注解。
@Aspect //表示我们这个Logger类是一个切面增强类。
public class Logger {
@Before("execution(* com.itheima.service.*.*(..))")
public static void log(){
System.out.println("输出日志了~~");
}
}
二、 JDBC模板
Spring 由于所处的位置比较尴尬,刚好处在三层的中间位置。 它除了弄好自己的IOC + AOP之外, 为了更好的推广自己,让自己的生命力变得更持久, 也对持久层的框架做出了支持。 说白了,就是包装了dao层的框架代码。让程序员编程效率更高点。
对持久化框架的支持有以下几个
如果dao层采用的是JDBC来实现,那么spring会提供 JDBC模板
如果dao采用的是hibernate来实现,那么spring也会随之提供hibernate模板
Mybatis也是如此..
1. jdbc模板 入门 – 以向数据库插入数据为例子
导入jar包
额外导入 三个jar包
spring-jdbc-xxx.jar
spring-tx-xxx.jar (事务的包)
jdbc的驱动包写代码
如何写这段代码:
只需要记住一个关键的类名:JdbcTemplate即可,后续可以通过这个类,添加响应的dataSource,然后进行插入
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///user");
dataSource.setUsername("root");
dataSource.setPassword("root");
//1. 创建jdbc模板实例 和dbutils挺像的。 操作数据库,也是两个方法。
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//设置数据源
jdbcTemplate.setDataSource(dataSource);
String sql = "insert into t_user values (null , ? , ?)";
jdbcTemplate.update(sql, "admin","admin");
2. jdbc模板 CRUD
jdbc模板针对查询的操作,稍微要注意下即可,需要我们手动封装数据。
- insert
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///user");
dataSource.setUsername("root");
dataSource.setPassword("root");
//1. 创建jdbc模板实例 和dbutils挺像的。 操作数据库,也是两个方法。
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//设置数据源
jdbcTemplate.setDataSource(dataSource);
String sql = "insert into t_user values (null , ? , ?)";
jdbcTemplate.update(sql, "admin","admin");
- delete
@Test
public void test01(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/db_for_test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "delete from user where id = ?";
jdbcTemplate.update(sql, 6);
}
- update
@Test
public void test02(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/db_for_test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//String sql = "insert into user values(null,?,?)";
String sql = "update user set address = ? where id = ?";
jdbcTemplate.update(sql,"地球",5);
}
查询总条数 - 稍微麻烦一点,功能没有JDBC强,勉强可用
JdbcTemplate这个类进行查询的时候,不会将结果封装成为我们需要的对象,需要我们手动进行封装查询总记录条数
@Test
public void test03(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/db_for_test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "select count(*) from user";
int result = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(result);
}
- 查询单个对象
@Test
public void test04(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/db_for_test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "select * from user where id = ?";
User result = jdbcTemplate.queryForObject(sql, new MyRowMapper(),5);
System.out.println(result);
}
class MyRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet paramResultSet, int paramInt) throws SQLException {
int id = paramResultSet.getInt("id");
String name = paramResultSet.getString("name");
String address = paramResultSet.getString("address");
User user = new User();
user.setAdd(address);
user.setName(name);
return user;
}
}
- 查询list集合
为什么不用jdbcTemplate.queryforList()方法呢?因为里面没有RowMapper这个参数
@Test
public void test04(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/db_for_test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "select * from user";
List<User> list= jdbcTemplate.query(sql, new MyRowMapper());
System.out.println(list);
}
class MyRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet paramResultSet, int paramInt) throws SQLException {
int id = paramResultSet.getInt("id");
String name = paramResultSet.getString("name");
String address = paramResultSet.getString("address");
User user = new User();
user.setAdd(address);
user.setName(name);
return user;
}
}
3. jdbc模板 注入写法 – 使用测试整合UserService - UserDao - JDBCTemplate - C3p0连接池
导包:
导入Spring四个核心jar包,Spring-jdbc-xx.jar ,Spring-tx-xxx.jar, jdbc驱动, Spring-test-xxx.jar
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class testSpring模板整合service层和dao层 {
@Autowired
private UserService userService;
@Test
public void test01(){
userService.save();
}
}
- service代码
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao ;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("调用了userServiceImpl的save方法");
userDao.save();
}
}
- dao代码
public class UserDaoImpl implements UserDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void save() {
String sql = "insert into t_user values (null , ? , ?)";
jdbcTemplate.update(sql, "zhangsan","123456");
System.out.println("保存完毕~");
}
}
- 配置
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"
scope="prototype">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<!-- ref : 告诉spring要new哪一个类给注入进去 要想表示这个类, 得声明一个bean -->
<property name="jdbcTemplate" ref="jt"></property>
</bean>
<bean id="jt" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"></property>
</bean>
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///user"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
4. jdbc模板 关联c3p0连接池
优化使用:使用c3p0来优化上面案例的模板的使用
代码不用改动,只要改动注入的数据源即可
首先导入c3p0连接池的jar包
配置文件:
<?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 id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db_for_test"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<context:component-scan base-package="com.itheima"></context:component-scan>
</beans>
三、事务
- 什么是事务
事务其实就是用来包装一组逻辑,控制逻辑的整体结果。 如果这一组逻辑里面有一个动作执行失败, 那么整组的结果就以失败告终。(回滚事务) , 如果这一组逻辑里面运行都成功了,那么就认为成功(提交事务)
事务特性
ACID 原子性 、一致性、隔离性、 持久性
事务如果不考虑隔离会出现以下问题
读问题
脏读
不可重复读
虚读|幻读隔离级别:
读未提交
读已提交 : Oracle
可重复读 : Mysql
序列化|串行化写问题
丢失更新
两个事务同时操作。 不管后面的那个事务是提交还是回滚,都将让前一个事务提交的结果,失效。丢失。乐观锁
认为不会出现丢失更新, 要在表里面加一个字段,自己维护。
悲观锁
还没开始做,就认为一定会丢失更新。
在开始操作之前,先执行查询 for update
1. 编程式
导包:
Spring-jdbc-XXX.jar
Spring-tx-xxx.jar
当然还有jdbc驱动包
主要的类就是TransactionTemplate和JdbcTemplate
@Test
public void testDemo(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///user");
dataSource.setUsername("root");
dataSource.setPassword("root");
//定义事务的管理者
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
//创建事务的模板
TransactionTemplate transactionTemplate = new TransactionTemplate();
//设置事务的管理者是谁。一定要设置管理者,否则无法使用事务。
transactionTemplate.setTransactionManager(transactionManager);
final JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
//设置回调,也就是在里面写我们真正要操作的 crud逻辑
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus arg0) {
try {
//在这里面写 crud代码。 这个doInTransaction 方法会被spring的框架所调用。
//它在底下开启完事务之后,就用这个方法。
String sql = "insert into t_user values (null , ? , ?)";
jdbcTemplate.update(sql , "aobama2","6666222");
int a = 10 / 0 ;
} catch (DataAccessException e) {
e.printStackTrace();
//设置回滚的标记。
arg0.setRollbackOnly();
}
return null;
}
});
}
2. 声明式 - XML
- 导入jar包
Spring-jdbc-XXX.jar
Spring-tx-xxx.jar
当然还有jdbc驱动包 导入约束
aop \ tx \bean 约束配置事务
可以看出,事务的本质也是进行了aop的增强,在前面加上了事务的开启,后面加上了事务的提交和回滚
<!-- 以下是配置事务 事务的配置,其实就是AOP的操作。 -->
<!-- 1. 声明事务的建议 会对哪一个方法进行事务啊。 就是在这里进行配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds"></property>
</bean>
<tx:advice id="advice" transaction-manager="transactionManager">
<!-- 这个tx:attributes 就是用来控制到底给哪个方法加入事务 -->
<tx:attributes>
<!-- * 表示给前面找到的所有方法都应用事务 dao ==== jdbctemplate hibernatetemplate-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 还少一件事情, 对哪些类的,哪些方法,进行事务。 -->
<aop:config>
<aop:pointcut expression="execution(* com.itheima.service.*.*(..))" id="pointCut"/>
<!-- 给上面的pointCut 切入点,找到的方法,应用上面给出的事务建议。也就是给这些方法,都是用事务来管理 -->
<aop:advisor advice-ref="advice" pointcut-ref="pointCut"/>
</aop:config>
3. 声明式 - 注解(重要 - 最常用的方式)
- 在xml里面指定事务使用的管理者是谁
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db_for_test"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!-- 打开注解开关 这里是指定注解的事务,采用什么管理者。 光有那个类上的注解,是不够的,因为spring
也不知道我们打算使用什么类型的管理者来操作事务。 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
2. 在业务逻辑类上面打上注解 ,当然如果想指定具体的某一个方法采用事务,也可以在方法上面打上注解
public class UserDaoImpl implements UserDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
@Transactional
public void save() {
System.out.println("userDao的save方法执行了");
String sql = "insert into user values(null,?,?)";
jdbcTemplate.update(sql, "测试1","测试1");
int i= 10/0;
jdbcTemplate.update(sql, "测试2","测试2");
System.out.println("保存成功");
}
}