spring整合jdbc(管理数据源——使用JDBCTemplate实现)
spring中对JDBC也做了一些整合,让我们可以通过配置文件就实现JDBC的使用,加上JDBCTemplate,就可以实现一行crud操作(ps:其实这是一句废话,之后会用mybatis,也不会用JDBCTemplate,但是!今天一会所写的spring事务管理就很重要),下面用代码来告诉大家如何使用spring整合jdbc。
学习前提:构建三层架构,之前已经构建了很多次了,这次就不再做详细解说了。
1.导入jar包:
其中包括spring的jar包,还有c3p0连接池和jdbc的jar包
2.书写配置文件:
配置数据源的时候,clss中的值是c3p0的特定位置,简单说就是写死的,其中还需要配置数据库的相关信息。配置JDBCTemplate中的class也是写死的,另外还需要配置JDBCTemplate。
3.书写DAO层中的Mysql类中的代码:
代码可以看出,原来写jdbc需要获取的连接,传输器都被简化了,只要声明一个JdbcTemplate对象并注入,就可以通过一个updata来进行操作了
@Repository("mysql")
public class MySql implements DAO {
//声明JdbcTemplate变量并注入
@Autowired
private JdbcTemplate JdbcTemplate=null;
@Override
public void addUser(User user) {
//使用JdbcTemplate执行添加sql
JdbcTemplate.update("insert into user values (?,?,?)",user.getId(),user.getName(),user.getAge());
}
@Override
public User queryUser(int id) {
//用 BeanPropertyRowMapper<User>(User.class)反射出user中的列创建一个bean,然后把查询结果放入bean中返回
return JdbcTemplate.queryForObject("select * from user where id=?",new BeanPropertyRowMapper<User>(User.class),id);
}
}
web和service层:
这两层正常调用就可以。
spring jdbc的事务管理(通过注解的形式)
spring中对事务的管理进行了封装,通过注解的形式,就可以在service层中设置哪些方法是需要事务管理的需要回滚的还可以通过注释设置需要回滚声明类型的异常。
1.写配置文件:
2.在service层中,需要添加事务的方法上添加@Transactional()括号中可以添加相关的字段来进行控制,这里说几个:
rollback-for——用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
no-rollback- for——抛出 no-rollback-for 指定的异常类型,不回滚事务。
**需要特殊说明:**该事务管理,只会处理运行时异常,如果不指定rollback-for属性,是不会处理运行时异常,但是可以写成像图片中一样,这样的话就会处理运行时异常了
执行过程:web层调用service层中的addUser方法,service层中调用dao层中的addUser方法,同时开启事务,只有方法完全执行完,并且没有任何问题,才会提交事务,否则回滚事务。
ThreadLocal处理线程安全问题
当多个线程获取connect并发的去进入写操作的事务管理中,如果大家使用的是同一个connect,就会产生线程并发安全问题,怎么处理这种问题?那么首先我们需要先了解一下线程安全问题产生的三个条件:
1.有多个并发同时进行
2.有共享的资源
3.有写操作
第一个解决方法,也是最常想到的,那就是加锁,给大家共享的connect去加锁,但是加锁就意味着效率会降低。
第二个解决办法,也是今天所要说的,就是使用ThreadLocal来处理这个问题
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(){
@Override
protected Connection initialValue() {
return JDBCUtils.getConn();
}
};
每当一个线程进入到事务中, ThreadLocal会把该线程,和一个从c3p0连接池里拿出来的connect连接放入一个map中,这样每次不同的线程进来,就可以从连接池中拿到属于自己的连接,这样就可以避免事务出错。
**
一个小知识记录:
**
如何自定义注解:
//注解可以写的位置声明
@Target(ElementType.METHOD)
//注解作用于什么地方,一般都写这里
@Retention(RetentionPolicy.RUNTIME)
//@interface写死
public @interface Trans {
}
关于切点表达式的一个补充:
//切出cn.tedu.service下所有包和子孙包中的类,没有参数函数名返回值限制 且 含有Trans注解的函数
@Around("execution(* cn.tedu.service..*(..)) && @annotation(tx)")
public Object myAround(ProceedingJoinPoint pjp, Trans tx) throws Throwable {
try {
TransactionManager.startTran();
Object retObj = pjp.proceed();
TransactionManager.commitTran();
return retObj;
} catch (Throwable e) {
TransactionManager.rollbackTran();
e.printStackTrace();
throw new RuntimeException(e);
} finally {
TransactionManager.release();
}
}