1.Service业务逻辑层:
1.业务概述:
代表用户完成的一个业务功能,可以由一个或多个DAO的调用
组成。(软件所提供的一个功能都叫业务)
2.Service开发流程
2.事务:
在JDBC中,在Service层实现事务需要以下几个步骤:
获得Connection对象开始事务–提交或回滚–关闭连接。其事务策略是
- 获得Connection对象开始事务:
connection.setAutoCommit(false);//true等价于1,false等价于0,将自动提交事务改为手动提交事务- 提交或回滚
connection.commit();//手动提交事务
connection.rollback();//手动回滚事务- 关闭连接
connection.close()
3.问题:
对于Service层获得的数据库连接,其与DAO层所获得的数据库连接直接操作数据库的连接并不相同,所以Service层数据库连接提交或回滚并不影响DAO层的数据库连接!!!
4.问题解决办法:
1.解决方案1:传递Connection
为了解决线程中Connection对象不同步的问题,可以将Connection对象通过service传递给各个DAO方法,实现Service层数据库连接与DAO层数据库连接相同,实现事务的回滚与提交
|
传递Connection出现的问题:
- 如果使用传递Connection,容易造成接口污染(
不同的数据库厂商和框架的数据库连接不同,不一定是Connection
)。- 定义接口是为了更容易更换实现,从而实现面向接口编程,而将Connection定义在接口中,会造成污染当前接口。
2.解决方案2:ThreadLocal
- 可以将整个线程中(单线程)中,存储一个共享值。
- 线程拥有一个类似Map的属性,键值对结构<ThreadLocal对象,值>。
- 一个线程共享同一个ThreadLocal,在整个流程中任一环节可以存值或取值。
当在线程中存储connection连接时,Service与DAO用的connection连接相同,所以在DAO层用完connection后不要关闭,最后在Service层关闭connection
完善!!!:
为保证程序各层各司其职,Service层不应该出现connection连接对象,更不应该释放资源,所以将代码封装在DBUtils中!!!!
5.示例代码:
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class DBUtils {
private static final Properties PROPERTIES=new Properties(); //存储配置文件的map
//Driver只加载一次,避免调用getConnection()方法时,检查Driver类是否已加载到程序中!
static {
InputStream is= DBUtils.class.getResourceAsStream("/db.properties");//通过复用文本自带流,读取配置文件
try {
PROPERTIES.load(is); //通过properties对象,将流中配置信息分成键值对
String driverName=PROPERTIES.getProperty("driver"); //通过键获取值
Class.forName(driverName); //加载驱动类
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//创建一个线程,达到一个程序共享连接
private static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();
//1.获取连接
public static Connection getConnection(){
//将当前线程中绑定的connection对象,赋值给connection
Connection connection=threadLocal.get();
try {
if(connection==null){
String url=PROPERTIES.getProperty("url");
String username=PROPERTIES.getProperty("username");
String password=PROPERTIES.getProperty("password");
connection= DriverManager.getConnection(url,username,password);
//把连接存在当前共享的线程中
threadLocal.set(connection);
}
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
//开启事务
public static void begin(){
Connection connection=getConnection();
try {
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交事务
public static void commit(){
//所有连接都是通过ThreadLocal获得,连接是同一个连接
Connection connection=null; // 防止在此处出现异常
try {
connection=getConnection();
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//提交事务后应该释放资源
closeAll(connection,null,null);
}
}
//回滚事务、
public static void rollback(){
Connection connection=null;
try {
connection=getConnection();
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}finally {
closeAll(connection,null,null);
}
}
//2.释放资源
public static void closeAll(Connection connection, Statement statement, ResultSet resultSet){
try {
if(resultSet!=null){
resultSet.close();
}
if(statement!=null){
statement.close();
}
if(connection!=null){
connection.close();
threadLocal.remove(); // 关闭连接后,移除已关闭的connection对象!!!
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}