spring-jdbc
需要的jar包:
mysql-connector-java
c3p0
spring-jdbc
spring-context
1.spring-jdbc中xml配置
1.1引入jdbc配置文件:
有两种方式
<!-- 方式1 contxt -->
<contxt:property-placeholder location="classpath:db.properties"></contxt:property-placeholder>
<!--方式2 util-->
<util:properties id="db" location="classpath:/db.properties"></util:properties>
1.2配置数据源(连接池)
要设置参数,交给spring容器管理
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="user" value="#{db.username}"></property>
<property name="password" value="#{db.password}"></property>
<property name="jdbcUrl" value="#{db.url}"></property>
<property name="driverClass" value="#{db.driver}"></property>
</bean>
1.3配置JdbcTemplate
交给容器管理
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
1.4开启包扫描和注解支持
<!--开启包扫描-->
<contxt:component-scan base-package="com.iweb.dao"></contxt:component-scan>
<!--开启注入注解支持-->
<contxt:annotation-config></contxt:annotation-config>
2.JdbcTemplate类,及常用api
针对数据库的操作,Spring框架提供了JdbcTemplate类,该类是Spring框架数据抽象层的基础,可以说,JdbcTemplate类是SpringJDBC的核心类
2.1execute
execute方法主要用来建表相关操作,平时很少使用
String sql = "create table test (id integer, name varchar(100))";
jdbcTemplate.execute(sql);
2.2DML语句(表的增删改)
DML语句返回结果:影响的行数
增删改操作,都调用JdbcTemplate的update方法,括号内传一个sql语句即可
添加用户:
update会返回受影响的行数,添加时不能直接传入一个实体数据,内部无法自动取值,如果用?来占位,需要每个?都单独的去指定值
public void addUser(User user){
int update = jdbcTemplate.update("insert into user(name,age) values(?,?)", user.getName(),user.getAge());
System.out.println(update);
}
更新用户:
public void update(User user){
int update = jdbcTemplate.update("update user set name=?,age=? where id=?",
user.getName(), user.getAge(), user.getId());
System.out.println(update);
}
删除用户:
实际业务中,删除操作不会真正删除数据,而是将数据变为删除状态
public void delete(Integer id){
int update = jdbcTemplate.update("delete from user where id =?", id);
System.out.println(update);
}
2.3DQl语句(表查询)
query:
1.通用的查询方法,有多个同名方法的重载,使用BeanPropertyRowMapper做映射将返回对象封装成List。
2.BeanPropertyRowMapper对象会将数据库查询回的ResultSet自动封装为一个javaBean对象
3.底层采用代理机制,javaBean中的字段名必须和数据库的字段名一致,否则无法自动映射
query重载的方法有很多,常用的是传3个参数
依次是sql语句,Object的数组,BeanPropertyRowMapper对象
public User getUserById(Integer id){
List<User> list = jdbcTemplate.query("select * from user where id=?", new Object[]{id},new BeanPropertyRowMapper<User>(User.class));
return list.get(0);
}
queryForMap:
特点:
1.返回Map<String,Object>的查询结果,其中键是列名,值是表中对应的记录。
2.用于查询结果只有1条记录的情况。
3.返回多条记录报异常
public Map<String, Object> getALl(){
Map<String, Object> map = jdbcTemplate.queryForMap("select * from user where id =?", 30021);
System.out.println(map);
return map;
}
queryForList:
特点:
1.返回多条记录的查询结果,封装成一个List集合
2.默认List集合中的每个元素是Map对象,即List<Map<String,Object>>
3.如果要封装成List对象,用query()方法。
public List<Map<String,Object>> queryForList(){
List<Map<String, Object>> maps = jdbcTemplate.queryForList(
"select * from user1");
return maps;
}
测试:
@Test
public void test1(){
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-jdbc.xml");
UserDao userDao = context.getBean("userDao", UserDao.class);
System.out.println(userDao.queryForList());
//[{id=1, name=张三, age=18}, {id=2, name=李四, age=20}]
}
用RowMapper封装bean:
RowMapper接口定义了对象到列的映射关系,可以帮助我们在查询时自动封装bean
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
List<User> user = jdbcTemplate.query("select * from user where id = ?"
,new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int index) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
return user;
}
} ,
2);
System.out.println(user);
3.Spring 整合JDBC事务管理
3.1编程式事务管理
手动编写代码,在需要开启事务的地方手动控制事务的提交与回滚,编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate
3.2声明式事务管理
spring提供事务管理,配置方式(AOP实现)
1.导入配置文件
<!--导入配置文件-->
<util:properties id="db" location="db.properties"></util:properties>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
2.配置数据源
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="#{db.url}"></property>
<property name="user" value="#{db.username}"></property>
<property name="password" value="#{db.password}"></property>
<property name="driverClass" value="#{db.driver}"></property>
</bean>
3.配置事务管理器
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--事务管理器,需要指定数据源
此处就是去找id为dataSource的数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
4.配置事务通知
<!--配置事务通知,需要指定一个事务管理器
此处的事务管理器是id为transactionManager的管理器-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="update*"></tx:method>
<tx:method name="delete*"></tx:method>
</tx:attributes>
</tx:advice>
5.配置事务切面
<!--配置事务切面,需要指定一个事务通知
此处就是id为txAdvice的事务通知-->
<aop:config>
<!--execution(* com.iweb.service.*.*(..)) 表示service包下所有类的所有方法都有效-->
<aop:pointcut id="pc" expression="execution(* com.iweb.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>
3.3回滚机制
SpringJDBC内置的事务策略,只在底层抛出的异常是运行时异常时,才会回滚,其他异常不回滚,留给用户手动处理。
也可以在配置中指定在原有规则的基础上,哪些异常额外的回滚或不回滚
注意:
1.如果在一个业务逻辑中,需要有多步不同表的操作,此时应该在service层中完成对不同表的操作,以此保证多步操作处在同一个事务中。
2.切勿将业务代码在web层中调用不同Service来实现,虽然正常情况下也可以完成功能,但一旦出问题,很可能只有部分操作被回滚,数据出问题。
3.出现这种问题,不是事务管理机制的问题,而是开发者将业务逻辑错误的在web层中进行了实现,所以切记,web层只负责和用户交互和业务逻辑的调用,不进行任何业务逻辑的处理,任何业务逻辑都在service层中进行。
3.4注解方式
1.配置数据源
<!--导入配置文件-->
<util:properties id="db" location="db.properties"></util:properties>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="#{db.url}"></property>
<property name="user" value="#{db.username}"></property>
<property name="password" value="#{db.password}"></property>
<property name="driverClass" value="#{db.driver}"></property>
</bean>
2.配置事务管理器
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--事务管理器,需要指定数据源
此处就是去找id为dataSource的数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
3.开启包扫描和注解支持
<!--开启包扫描-->
<contxt:component-scan base-package="com.iweb"></contxt:component-scan>
<!--开启注解注入-->
<contxt:annotation-config></contxt:annotation-config>
4.开启事务注解支持
<!--开启事务注解支持-->
<tx:annotation-driven></tx:annotation-driven>
5.在需要事务支持的位置加上@Transactional注解
@Transactional:
1.声明式事务管理,应用在方法上时,这个方法的业务操作,spring会自动帮我们控制事务
2.只有运行时异常才能回滚事务
rollbackFor 可以指定什么异常回滚事务,例如:
//此处指定,只要是异常就回滚
@Transactional(rollbackFor = {java.lang.Throwable.class})
noRollbackFor 指定什么异常不回滚事务
propagation 可以指定事务的传播机制
propagation = Propagation.NOT_SUPPORTED 表示这个方法不支持事务,一般用在查询方法上
@Transactional(rollbackFor = {java.lang.Throwable.class},
propagation = Propagation.NOT_SUPPORTED )
4.作用在类上时,表示这个类中所有方法的事务管理都有效
此时如果某个方法不需要事务,可以指定propagation = Propagation.NOT_SUPPORTED
3.5事务的传播机制
PROPAGATION_REQUIRED:
Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行
PROPAGATION_NOT_SUPPORT:
该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码
缓存
/**
* 用户数据的缓存
*/
@Component
@Aspect
@Scope("singleton")
public class UserCache {
//此map用来存储缓存数据
/**
* getArgs()获取目标方法的参数 ,返回的是一个Object数组
*/
private Map<Integer,User> cache = new HashMap<>();
@Around("execution( * com.iweb.dao.UserDaoMysqlImpl.getUserById(..))")
public User cache(ProceedingJoinPoint pjp) throws Throwable {
//获取目标参数,业务:获取查询数据库的id
Integer id = (Integer)pjp.getArgs()[0];
if(cache.containsKey(id)){//如果缓存中已经缓存这个id的user对象,返回这个缓存的user对象
System.out.println("使用缓存的数据");
return cache.get(id);
}
System.out.println("查询数据库");
User user = (User)pjp.proceed();//调用目标方法,实际调用dao查询数据库
cache.put(id,user);//向缓存中添加数据
return user;
}
}