Java基础29–JDBCTools
为什么要工具类:
每次·连接数据库有些操作都是必写的,会有很多重复或者相似的代码,将这些代码提出来
一般都用连接池
JDBCTools第一个版本
public class JDBCToolsV1 {
private static DataSource ds;//ds创建要么直接声明时初始化,要么static代码块里初始化,这里后面一种
static{
try {
//静态代码块
Properties pro = new Properties();
pro.load(TestPools.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//抛出编译时异常
public static Connection getConnection() throws SQLException{
//方式一:DriverManager.getConnection()
//方式二:连接池对象.getConnection(),基本用这个
return ds.getConnection();
}
//把编译时异常转为运行时异常
public static void free(Connection conn){
try {
if(conn != null){
conn.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);//转成运行时异常
}
}
//这个方法只能用于不需要处理事务的情况
//增、修改、删除、通用的update
public static int update(String sql,Object... args) throws SQLException{
//Object... args 0....n个?
//获取连接
Connection conn = getConnection();
//创建PreparedStatement
PreparedStatement ps = conn.prepareStatement(sql);
//设置?
if(args!=null && args.length>0){
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1, args[i]);
}
}
//执行sql
int len = ps.executeUpdate();
ps.close();
free(conn);
return len;
}
@Test
public void test01() throws SQLException{
//添加一个部门到test库的t_department
String sql = "INSERT INTO `t_department` VALUES(NULL,?,?) ";
int len = JDBCToolsV1.update(sql, "测试部门1" ,"测试部门简介1");
System.out.println(len>0?"success":"fail");
}
}
运行成功
若是事务
@Test
public void test02() throws SQLException{
String sql1 = "UPDATE t_department SET description = 'xx' WHERE did = 5183";
String sql2 = "UPDATE t_department SET description = 'yy' did = 5";//这个sql是故意写错的
//希望它俩是构成一个事务的
//直接用这个,没法处理事务
//要么同时成功。要么同时失败
JDBCToolsV1.update(sql1);
JDBCToolsV1.update(sql2);
}
会发现一个成功,一个失败,直接用这个,没法处理事务,说明这个update方法只能用于不需要处理事务的情况
若是不要上面那个update方法,让它处理事务
@Test
public void test03() throws SQLException{
String sql1 = "UPDATE t_department SET description = 'xx' WHERE did = 5183";
String sql2 = "UPDATE t_department SET description = 'yy' where did = 5";//这个sql是故意写错的
//希望它俩是构成一个事务的
Connection conn = JDBCToolsV1.getConnection();
conn.setAutoCommit(false);
PreparedStatement pst1 = conn.prepareStatement(sql1);
PreparedStatement pst2 = conn.prepareStatement(sql2);
try {
int len1 = pst1.executeUpdate();
int len2 = pst2.executeUpdate();
if(len1>0 && len2>0){
conn.commit();
}else{
conn.rollback();
}
} catch (Exception e) {
conn.rollback();
}
pst1.close();
pst2.close();
conn.setAutoCommit(true);
JDBCToolsV1.free(conn);
}
运行正常,但是还是有很多重复代码
若是写为
public static int update2(String sql,Object... args) throws SQLException{
//获取连接
Connection conn = getConnection();
//conn.setAutoCommit(false);//若是在这里设置,不能保证测试方法里两个sql是同一个事务,test05测试不通过
//创建PreparedStatement
PreparedStatement ps = conn.prepareStatement(sql);
//设置?
if(args!=null && args.length>0){
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1, args[i]);
}
}
//执行sql
int len = 0;
try {
len = ps.executeUpdate();
conn.commit();
} catch (Exception e) {
conn.rollback();
}
ps.close();
conn.setAutoCommit(true);
free(conn);
return len;
}
@Test
public void test04()throws Exception{
String sql1 = "UPDATE t_department SET description = 'xx' WHERE did = 5183";
String sql2 = "UPDATE t_department SET description = 'yy' where did = 5";//这个sql是故意写错的
//希望它俩是构成一个事务的
Connection conn = JDBCToolsV1.getConnection();
conn.setAutoCommit(false);
try {
int len1 = JDBCToolsV1.update(conn, sql1);
int len2 = JDBCToolsV1.update(conn, sql2);
if(len1 > 0 && len2>0){
conn.commit();
}else{
conn.rollback();
}
} catch (Exception e) {
conn.rollback();
}
conn.setAutoCommit(true);
JDBCToolsV1.free(conn);
//可以正常运行,但不规范
}
@Test
public void test05()throws Exception{
String sql1 = "UPDATE t_department SET description = 'xx' WHERE did = 5183";
String sql2 = "UPDATE t_department SET description = 'yy' did = 5";//这个sql是故意写错的
// JDBCToolsV1.update2(sql1);
// JDBCToolsV1.update2(sql2);
}
ThreadLocal类介绍
JDK 1.2的版本中就提供java.lang.ThreadLocal,为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。通常用来在在多线程中管理共享数据库连接、Session等
ThreadLocal用于保存某个线程共享变量,原因是在Java中,每一个线程对象中都有一个ThreadLocalMap<ThreadLocal, Object>,其key就是一个ThreadLocal,而Object即为该线程的共享变量。而这个map是通过ThreadLocal的set和get方法操作的。对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
public class TestTools{
public static void main(String[] args) {
MyThread m1 = new MyThread();
m1.start();
MyThread m2 = new MyThread();
m2.start();
}
}
class Tools {
private static Random rand = new Random();//产生随机数的工具类
private static ThreadLocal<Integer> th = new ThreadLocal<Integer>();
public static void setNumber(){
th.set(rand.nextInt(100));//这里设置的就是当前线程保存的共享变量,这个共享变量不是我们之前说的多个线程之间共享,是说的是同一个线程在整个生命周期中,使用的共享变量
}
public static int getNumber(){
return th.get();
}
}
class MyThread extends Thread{
public void run(){
Tools.setNumber();
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int number = Tools.getNumber();
System.out.println(Thread.currentThread().getName() +":"+ number);
}
}
}
不管什么时候拿,不管生命周期过久,同一个线程对象拿的值始终是相等的
JDBCTools第二个版本
public class JDBCToolsV2 {
private static DataSource ds;
private static ThreadLocal<Connection> th;
static{
try {
//静态代码块
Properties pro = new Properties();
pro.load(TestPools.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
th = new ThreadLocal<Connection>();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//抛出编译时异常
public static Connection getConnection() throws SQLException{
//方式一:DriverManager.getConnection()
//方式二:连接池对象.getConnection()
Connection conn = th.get();//获取当前线程中的共享的连接对象
// conn.setAutoCommit(false);不能写这里,可能会重复
if(conn == null){//当前线程没有拿过连接,第一个获取连接
conn = ds.getConnection();//从连接池中拿一个新的
th.set(conn);//放到当前线程共享变量中
}
return conn;
}
//把编译时异常转为运行时异常
public static void free(Connection conn){
try {
if(conn != null){
conn.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static int update(String sql,Object... args) throws SQLException{
Connection conn = getConnection();
PreparedStatement pst = conn.prepareStatement(sql);
if(args!=null && args.length>0){
for (int i = 0; i < args.length; i++) {
pst.setObject(i+1, args[i]);
}
}
int len = pst.executeUpdate();
pst.close();
//conn.setAutoCommit(true);不能写这里,可能会重复
return len;
}
}
public static void main(String[] args) throws SQLException {
String sql1 = "UPDATE t_department SET description = 'xx' WHERE did = 5183";
String sql2 = "UPDATE t_department SET description = 'yy' where did = 5";//这个sql是故意写错的
Connection conn = JDBCToolsV2.getConnection();
conn.setAutoCommit(false);
try {
JDBCToolsV2.update(sql1);
JDBCToolsV2.update(sql2);
conn.commit();
} catch (Exception e) {
e.printStackTrace();
conn.rollback();
}
conn.setAutoCommit(true);
JDBCToolsV2.free(conn);
}
没有传connection对象,只是传来sql,但update方法里能保证是同一个对象