回顾JDBC
java操作关系型数据的API。
导入相关数据库的驱动包后可以通过JDBC提供的接口来操作数据库。
实现JDBC的六个步骤
注册数据库驱动
获取数据库连接
获取传输器对象
传输sql执行获取结果集对象
遍历结果集获取信息
关闭资源
package cn.tedu.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
create database springdb;
use springdb;
create table stu(id int primary key auto_increment,name varchar(255),addr varchar(255));
insert into stu values (null,‘aaa’,‘bj’),(null,‘bbb’,‘sh’),(null,‘ccc’,‘gz’),(null,‘ddd’,‘sh’),(null,‘eee’,‘bj’);
*/
public class Demo01 {
public static void main(String[] args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.注册数据库驱动
Class.forName(“com.mysql.jdbc.Driver”);
//2.获取数据库连接
conn = DriverManager.getConnection(“jdbc:mysql:///springdb”,“root”,“root”);
//3.获取传输器
ps = conn.prepareStatement(“select * from stu where id < ?”);
ps.setInt(1, 4);
//4.执行sql获取结果集
rs = ps.executeQuery();
//5.遍历结果集获取结果
while(rs.next()){
String name = rs.getString(“name”);
System.out.println(name);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.关闭资源
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally{
rs = null;
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
} finally{
ps = null;
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally{
conn = null;
}
}
}
}
}
数据库连接池(数据源)
C3P0连接池
package cn.tedu.jdbc;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
create database springdb;
use springdb;
create table stu(id int primary key auto_increment,name varchar(255),addr varchar(255));
insert into stu values (null,‘aaa’,‘bj’),(null,‘bbb’,‘sh’),(null,‘ccc’,‘gz’),(null,‘ddd’,‘sh’),(null,‘eee’,‘bj’);
*/
public class Demo02 {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
static{
try {
dataSource.setDriverClass(“com.mysql.jdbc.Driver”);
dataSource.setJdbcUrl(“jdbc:mysql:///springdb”);
dataSource.setUser(“root”);
dataSource.setPassword(“root”);
} catch (PropertyVetoException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static void main(String[] args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.2.从数据源中获取连接
conn = dataSource.getConnection();
//3.获取传输器
ps = conn.prepareStatement(“select * from stu where id < ?”);
ps.setInt(1, 4);
//4.执行sql获取结果集
rs = ps.executeQuery();
//5.遍历结果集获取结果
while(rs.next()){
String name = rs.getString(“name”);
System.out.println(name);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.关闭资源
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally{
rs = null;
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
} finally{
ps = null;
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally{
conn = null;
}
}
}
}
}
Spring整合JDBC - 管理数据源
导入相关开发包
计算机生成了可选文字:aopalliance-1.O.Jar a ’ pe 戲 」 一 17 胃 刂 ar ; aspectJweaver-1.7.4.Jar
capo . g 12 r commons-logging-I.O.4.Jar mysql connector-Java-5.1.10-bin.Jar
spring-aop-3.2.3.RELEASE.Jar spring-aspects-3.2.3.RELEASE.Jar
spring-beans-3.2.3.RELEASE.Jar spring-context-3.2.3.RELEASE.Jar
spring-context-support-3.2.3.RELEASE.Ja spring-core-3.2.3.RELEASE.Jar
’ rl -ex ress10n-3.2.3.RELEASE.Jar spring-jdbc-3.2 3.RELEASEjar
-test-3.2.3.RELEASE. 3 r spring-tx-3.2.3.RELEASE.Jar
将数据源交于Spring管理
计算机生成了可选文字:dbc.propertles driverC1ass:com . mysql . jdbc . Driver
jdbcUr1:jdbc : mysql : //localhost : 3386/spring user:root password:root
计算机生成了可选文字:引 入 配 置 文 件 一 一 〉
(context:property-placeholder location:"cLasspath:/jdbc.properties
配 置 据 源 一 一 〉
(bean id: ’ ’ da 亡 0S0 rce class:"com/mchange.v2.c3pe. ComboPooL edDataSource ’ 〉
(property name:“driverCLass” value:"
f
d
r
i
v
e
r
C
L
a
s
s
)
"
X
/
p
r
o
p
e
r
t
p
(
p
r
o
p
e
r
t
y
n
a
m
e
:
"
j
d
b
c
U
r
L
"
v
a
l
u
e
:
"
fdriverCLass)"X/propertp (property name:"jdbcUrL" value:"
fdriverCLass)"X/propertp(propertyname:"jdbcUrL"value:"fjdbcUrL)"X/propertY)
(property name:“user” value:"
f
u
s
e
r
)
"
x
/
p
r
o
p
e
r
t
y
)
(
p
r
o
p
e
r
t
y
n
a
m
e
:
a
s
s
劂
o
r
冒
"
v
a
l
u
e
:
"
fuser)"x/property) (property name: ass 劂 or 冒 " value:"
fuser)"x/property)(propertyname:ass劂or冒"value:"fpassword)"x/property) K/bean)
通过spring获取数据源,获取连接,操作数据库
package cn.tedu;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
- 通过spring管理数据源
*/
public class Demo01 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);
DataSource dataSource = (DataSource) context.getBean(“dataSource”);
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
ps = conn.prepareStatement(“select * from user where id = ?”);
ps.setInt(1, 2);
rs = ps.executeQuery();
while(rs.next()) {
String name = rs.getString(“name”);
System.out.println(name);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs = null;
}
}
if(ps!=null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
ps = null;
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
}
Spring整合JDBC - JDBC模板类
使用模版类能够极大的简化原有JDBC的编程过程。
在Spring中配置JDBC模板类
计算机生成了可选文字:
(bean id:“jdbcTempLate” class:"org/springframework.jdbc.core.JdbcTempLate
(property name: ’ ’ da 亡 0S0 rce ref:"dataSource"X/propertY) K/bean)
使用JDBC模板类实现增删改查
/**
- 查询 - 查询一个SqlRowSet,自己处理结果
*/
@Test
public void test06() {
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
SqlRowSet rs = jdbcTemplate.queryForRowSet("select * from user");
while(rs.next()) {
String name = rs.getString("name");
System.out.println(name);
}
}
/**
- 查询 - 查询一个Map,仅限于结果是一条记录时使用
*/
@Test
public void test05() {
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
Map<String, Object> map = jdbcTemplate.queryForMap("select * from user where id = 2");
System.out.println(map);
}
/**
- 查询 - 查询一个List
*/
@Test
public void test04() {
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from user");
System.out.println(list);
}
/**
- 删除
*/
@Test
public void test03() {
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
jdbcTemplate.update("delete from user where id = ?" ,4);
}
/**
- 修改
*/
@Test
public void test02() {
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
jdbcTemplate.update("update user set age = ? where id = ?",44,4);
}
/**
- 新增
*/
@Test
public void test01() {
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
jdbcTemplate.update("insert into user values (?,?,?)",4,"ddd",33);
}
使用RowMapper封装bean
RowMapper接口定义了对象到列的映射关系,可以帮助我们在查询时自动封装bean。
/**
- 查询 - 查询结果交给一个RowMapper处理,将结果封装为bean
*/
@Test
public void test08() {
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
User user = jdbcTemplate.queryForObject(
"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);
}
/**
- 查询 - 查询结果交给一个RowMapper处理,将结果封装为bean
*/
@Test
public void test07() {
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);
}
使用BeanPropertyRowMapper自动进行映射
BeanPropertyRowMapper内部可以使用指定类进行反射(内省)来获知类内部的属性信息,自动映射到表的列。
使用它一定要注意,类的属性名要和对应表的列名必须对应的上,否则属性无法自动映射。
BeanPropertyRowMapper底层通过反射(内省)来实现,相对于之前自己写的RowMapper效率比较低。
/**
-
查询 - 查询结果交给一个BeanPropertyRowMapper处理,将结果封装为bean
-
BeanPropertyRowMapper是RowMapper的实现
-
内部通过内省(反射)技术获取bean的所有bean属 性,对应获取查询结果中的同名列的值设置到bean中
-
最终返回封装了结果的bean
-
BeanPropertyRowMapper用起来方便,但效率相对低一些。
*/
@Test
public void test09() {
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean(“jdbcTemplate”);
User user = jdbcTemplate.queryForObject(“select * from user where id = ?”,new BeanPropertyRowMapper(User.class),2);
System.out.println(user);
}
Spring整合JDBC - 声明式事务处理
在之前的案例中,我们实现了自定义切面管理事务:
开发事务管理注解
开发事务管理切面
开发事务管理器TransactionManager
在事务管理器中通过ThreadLocal保证每个线程各自使用各自的Conn
事实上,SpringJDBC中提供了内置的事务处理机制,可以经过简单的配置完成事务管理,这称之为声明式事务处理。
创建项目,模拟MVC三层架构
略
在配置文件中导入相关约束
计算机生成了可选文字:K?xml version:“l.e” encoding:“UTF-8”?)
(beans xmlns:“http://www.springframework.org/schema/beans
xmlns : context: " h 亡 亡 : / / 鼽 . springframework . org/schema/context "
xmlns : aop: " h 亡 亡 : / / 鼽 . springframework . org/schema/aop
xmlns : tx: " h 亡 亡 : / / 鼽 . springframework . org/schema/tx”
xmlns : xsi: " h 亡 亡 : / / . . org/201/XMLSchema- instance
XSI : schemaLocation— . org/schema/beans
. org/schema/beans/spring-beans-3.2.xsd . org/schema/context
. org/schema/context/spring-context-3 . 2 . xsd . org/schema/aop
. org/schema/aop/spring-aop-3.2.xsd . org/schema/tx
. org/schema/tx/spring-tx-3.2.xsd h 亡 : / / 鼽 vw h 亡 : / / 鼽 vw h 亡 : / / 鼽 vw
h 亡 : / / 鼽 vw h 亡 : / / 鼽 vw h 亡 : / / 鼽 vw 向 亡 : / / 鼽 vw 向 亡 : / / 鼽 vw
. springframework . springframework . springframework . springframework
. springframework . springframework . springframework . springframework
配置事务管理器
计算机生成了可选文字:配 置 事 务 管 理 器 一 一 〉 (bean " 亡 亡 讠 on "
class: org.springframework.jdbc.datasource.DataSourceTransactionManager"
(property name: ’ ’ da 亡 0S0 rce ref:"dataSource"X/propertY) K/bean)
配置事务通知
计算机生成了可选文字:配 置 事 务 通 知 - (tx:advice id:"txAdvice
transaction—manager: " 亡 亡 讠 on ’ 〉 < t x : attributes)
’ ’ ad 冒 * ’ ’ propagation:“REQUIRED”/) (tx:method name: propagation:"REQUIRED’
(tx:method name:“deLete*” propagation: "REQUIRED ’ (tx:method name:“update*”
K/tx:attributes) K/tx:advice)
配置事务切面
计算机生成了可选文字:配 置 事 务 切 面 一 一 〉 GOP : config) Kaop:pointcut expression—
exec 亡 讠 on ( * Cn. . sprtng. service. * . * Kaop:advisor advice—ref:
’ ’ 亡 x 胃 L ’ 讠 ce ’ pointcut-ref:"pc’ / 〉 K/aop:confio
配置关系图
计算机生成了可选文字:配 置 据 源 一 一 〉
(bean id: " da 亡 0S0 C e " class:"com/mchange.v2.c3pe. ComboPooL edDataSource ’ 〉
“driverCLass” value:"
f
d
r
i
v
e
r
C
L
a
s
s
)
"
X
/
p
r
o
p
e
r
t
p
b
c
U
r
L
"
v
a
l
u
e
:
"
fdriverCLass)"X/propertp bcUrL" value:"
fdriverCLass)"X/propertpbcUrL"value:"fjdbcUrL)"X/propertY)
(property name:"use ’ value:"KaTeX parse error: Double superscript at position 27: …operty) ass ' '̲ value:"fpassword)"x/property)
mework.jdbc.d 0s0 rce . Da 亡 0S0 rce 丆 ransac 亡 讠 on anager " n (property nam
(property name: (property name: K/bean) 配 置 事 务 管 理 器 一 一 〉 (bean id:"transact
class: org.spr (property name: ’ ’ da 亡 0S0 K/bean) 配 置 事 务 通 知 -
ref: ’ ’ da 亡 0S0 rce "X/property) (tx:advice id:"txAdvice < t x : attributes)
(tx:method na e: (tx:method nam (tx:method name K/tx:attributes) K/tx:advice)
配 置 事 务 切 面 一 一 〉 GOP : config) transaction—manager: " 亡 亡 讠 on ’ 〉
’ ’ ad 冒 * ’ ’ pro agation:"REQUIRED’ 7 〉 ropagation: "REQUIRED ’ “deLete*”
ropagation: "REQUIRED ’ " da 亡 e * " xecution(* Cn. . service. * . *
’ ’ 亡 x 胃 vice pointcut-ref:‘pc’ / 〉 Kaop:pointcut expression— 卜 d : 乍 0 ’ 7
Kaop:advisor advice—ref: K/aop:confio
事务管理策略
异常的种类:
java.lang.Throwable
|-Exception
|-Runtime Exception
|-其他Exception
|-Error
SpringJDBC内置的事务策略,只在底层抛出的异常是运行时异常时,才会回滚,其他异常不回滚,留给用户手动处理。
也可以在配置中指定在原有规则的基础上,哪些异常额外的回滚或不回滚:
计算机生成了可选文字:配 置 事 务 通 知 - (tx:advice id:"txAdvice
transaction—manager: " 亡 亡 讠 on ’ 〉 < t x : attributes)
’ ’ ad * ’ ’ propagation:“REQUIRED’ (tx:method name: ’ 0 . .SQLException”
rollback-for— no-rollback-for:"Java. Lang . 胃 r 讠 亡 hme 亡 讠 CExce 亡 讠 on " / 〉
propagation:"REQUIRED’ (tx:method name:“deLete*” 7 〉 ktx:method name:“update*”
propagation:“REQUIRED” K/tx:attributes)
配置成如下形式,可以实现任意异常都自动回滚:
计算机生成了可选文字:配 置 事 务 通 知 - (tx:advice id:"txAdvice
transaction—manager: " 亡 亡 讠 on ’ 〉 < t x : attributes) ktx:method name:
(tx:method name:“deLete*” K/tx:attributes) K/tx:advice) ro a ation:“RE
rollback-for— ’ 口 at ’ 0 . L ang . ThrowabLe propagation
(tx:method name:“update*” propagation UTRED” "REQUIRED " "REQUIRED "
注意:如果在一个业务逻辑中,需要有多步不同表的操作,此时应该在service层中完成对不同表的操作,以此保证多步操作处在同一个事务中,切勿将业务代码在web层中调用不同Service来实现,虽然正常情况下也可以完成功能,但一旦出问题,很可能只有部分操作被回滚,数据出问题。
出现这种问题,不是事务管理机制的问题,而是开发者将业务逻辑错误的在web层中进行了实现,所以切记,web层只负责和用户交互和业务逻辑的调用,不进行任何业务逻辑的处理,任何业务逻辑都在service层中进行。
声明式事务处理 - 注解方式
开启事务注解配置
计算机生成了可选文字:‘bean id = ” 冒 0 亡 aSource ” class:"com/mchange. v2 . ComboPooLed
’ r 讠 verCL ass ” value:"com/mysqL.jdbc.Drive (property name:
(property name:"jdbcUrL ” value:"jdbc:mysql:///springdb’$
‘user ” value:"root’ x/property» (property name:
(property name: ass 射 or 冒 " value:"root’ x/property» K/bear»
计算机生成了可选文字:<!一酉己置事务管理器一><beanid二”t广ansact觉on河anage尸”
class二‘,o尸g.sp广觉n扩广a功e闪。广九.jdbc<propertyname二’‘datasou广ce’,
.datasou尸ce.Datasou广ceT尸ansact觉on河anage广”>ref二‘,datasou广ce">
计算机生成了可选文字:廾 启 spring 的 注 解 方 式 配 置 事 务 - (tx:annotation—driven/)
在方法上通过注解开启事务
即可以标注在接口上,也可以标注在实现类上,理论上应该表在接口上,实现面向接口编程,但实际开发中为了方便也有人写在实现类上。
计算机生成了可选文字:Transactional public void addUser(User user) throws SQLException;
计算机生成了可选文字:averride @TransactionaI
public void addlJser(User user) throws SQLException { userDao . addUser(user) ;
int i I/e; //throw new SQLException();
也可以在类上使用此接口,此时类中所有方法都会有事务
计算机生成了可选文字:Transactional
public class UserService 工 mp1 implements UserServ1ce {
当在类上开启了事务后,可以此类的方法中使用如下方式,控制某个方法没有事务
计算机生成了可选文字:averride @Transactiona1(propagation:Propagation . 0 丆 SUPPORTED)
public void addUser(User user) throws SQLException userDao . addUser(user) ;
int i I/e; //throw new SQLException();
通过注解控制事务时,和配置文件方式控制事务相同的是,默认只有运行时异常会回滚,非运行异常不回滚,此时可以通过如下注解选项额外配置 哪些异常需要回滚,哪些不需要。
计算机生成了可选文字:@Transactiona1 ac or: rowa e . C a S S n 0 0 ac
public void addUser(User user) throws SQLException userDao . addUser(user) ;
//int i 1/8; throw new SQLException(); or: xcep Ion.c a S S
扩展案例
**扩展案例:缓存的使用 - 通过AOP实现为查询操作增加缓存机制
@Component
@Aspect
public class CatchAspect {
/**
-
使用Map实现的缓存
-
只会增加不会减少
-
没有超时时间,不管过了多久都使用历史缓存
-
没有存储优化
*/
private Map<Integer,User> map = new HashMap<Integer,User>();
@Around(“execution(* cn.tedu.spring.service.*.queryUser(…))”)
public User around(ProceedingJoinPoint jp) throws Throwable{
int i = (Integer) jp.getArgs()[0];
if(map.containsKey(i)){
System.out.println(“使用缓存查询。。。”);
//有缓存
return map.get(i);
}else{
System.out.println(“使用数据库查询。。。”);
//没缓存
User user = (User) jp.proceed();
map.put(user.getId(), user);
return user;
}
}
}