【JDBC】JDBC实战

项目实战1:工具类(Util)、ROM、DAO设计模型

功能:
工具类Util:获得数据库连接,关闭资源
对象关系映射ROM:实体类(数据库表的映射)
数据访问对象DAO:数据的底层访问,增删改等
1).工具类(Util)
将加载配置文件的代码,转移到static代码块中.[只会在类加载的时候执行一次]
实现:
public class JdbcUtil {
	private static final Properties p= new Properties();
    static {  // 配置文件文件加载   执行一次.
		InputStream is = null; 	// 1. 输入流
		try {
			is = JdbcUtil3.class.getResourceAsStream("/com/baizhi/util/jdbc.properties");
			p.load(is);  // 2. load方法加载配置文件
		} catch (IOException e) {
			throw new RuntimeException("配置文件加载异常!", e);
		} finally {
			if (is != null) {
				try {	is.close();} 
catch (IOException e) {e.printStackTrace();}
			}
		}
	}
	public static Connection getConnection() {  //获得连接,返回
		Connection conn = null;
		try {
			Class.forName(p.getProperty("driverClassName")); // 1. 加载驱动
			conn = DriverManager.getConnection(p.getProperty("url"), p.getProperty("user"), p.getProperty("password"));// 2. 获得conn
		}catch (Exception e) {
			throw new RuntimeException("获得数据库连接异常",e);
		}
		return conn;
	}
	public static void close(ResultSet rs, PreparedStatement pstm,Connection conn) { //资源释放
		if (rs != null) {
			try {rs.close();}
catch (SQLException e) {	throw new RuntimeException("结果集资源释放异常", e);}
		}
		if (pstm != null) {
			try {pstm.close();} 
catch (SQLException e) {throw new RuntimeException("pstm资源释放异常", e);}
		}
		if (conn != null) {
			try {conn.close();}
catch (SQLException e) { new RuntimeException("连接资源释放异常", e); }
		}
	}
}
2).DAO、ORM模型设计
① ORM模式设计:O(Object) R(Relational) M(Mapping)
面向对象思想,作用是将数据库中的数据,封装成java对象,便于管理.

封装数据库数据的类设计原则:
Entity(实体): 封装表中的数据.   		Table:存储数据    
类(Person)                      		表(t_person)      
1个类创建的对象                		一行数据          
private 属性(变量命令:stuName)  		列/字段(stu_name) 
属性提供set/get方法                               
提供无参和有参构造方法                            
实体类实现Serializable接口                        

标准示例代码:
数据库表[创建]:
create table T_PERSON(
  id      NUMBER(10) primary key,
  name    VARCHAR2(50),
  sex     NUMBER(1),
  age     NUMBER(3),
  mobile  VARCHAR2(11),
  address VARCHAR2(200)
)

实体类[书写]:
public class Person implements Serializable{//表
    private Integer id;//字段id
    private String name;//字段name
    private Integer sex;//字段sex
    private Integer age;//字段
    private String mobile;//字段
    private String address;//字段
    
    public Person(){} //无参构造
    public Person(...){...}  //有参构造
    
    //get和set方法(必须标准的set和get)...
}
② DAO模式设计:D(Data) A(Access) O(Object)  数据访问对象
封装数据库(某张表)访问所有操作。 命名: XxxxDAO
功能:添加(将对象添加到数据库一行),删除,选择(选择后封装一个对象),选择所有(用List集合)
	事例:
public class PersonDAO {
	//删除数据库中person表中的1条数据根据id
    public void delete(Integer id){
        //1. 获得conn
        //2. 创建pstm : delete from t_person where id = ?
        //3. 绑定参数
        //4. 发送参数  执行sql
        //5. 释放资源
    }
}

项目实战2:软件三层架构(视图层(View)、业务逻辑层(Service)、数据访问层(DAO))

编码标准: 
1. 数据访问对象:DAO
   命令: XxxxDAO
   功能: 面向数据库操作,封装数据库访问.
   思路: 每个数据库访问,每个sql执行,对应DAO的一个方法.
   模板:
  public class XxxDAO{
      public void insert(实体){
          try{
               1. 获得连接
               2. 创建pstm  指定sql
               3. 绑定参数
               4. 发送参数,执行sql
          }catch(Exception e){
              throw new RunTimeException(e);
          }finally{ 5. 释放资源(不能关闭),Connection对象可在Service层关闭,但必须与Service层用同一个数据库连接对象 }
      }
      public void delete(id){ }    
      public void update(实体){ }
      public 实体 selectById(id){ }
      public List<实体> selectAll(){ }
   }
2. 业务对象: Service (Biz)
   命名: XxxxService
   功能: 面向业务实现用户的功能,实现事务
   思路: 一个功能对应一个方法   
   核心关注: ①service方法设计(函数名,参数,返回值)、②service方法的具体实现思路[调用dao]

   模板:
   public class XxxService{  
       //1. 一个业务功能对应Service一个方法.
       public 返回值  方法名(参数){
          调用dao完成业务功能  
//有事务控制时:需要Connection对象的setAutoCommit()/commit(),
//但必须和Dao获得的是统一个conn可以使用线程空间(ThreadLocal<T>)
代码下面有演示
       }
   }
3. 视图层:View
	展示给用户的界面,并且与service层交互。例如网页,可以操作查询等,
    
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
实例代码:
配置文件:conf.properties代码:
	driverClassName=oracle.jdbc.OracleDriver
url=jdbc\:oracle\:thin\:@localhost\:1521\:orcl
user=hr
password=Oracle123

工具类JdbcUtil代码: //在下面目录有连接池的使用
public class JdbcUtil {
	private static final Properties prop = new Properties();
	private static final ThreadLocal<Connection> tdl = new ThreadLocal<Connection>(); 
	static {
		InputStream is = null;
		try {
			is = JdbcUtil.class.getResourceAsStream("/com/baizhi/properties/jdbc.properties");
			prop.load(is);
			Class.forName(prop.getProperty("driverClassName"));
		} catch (Exception e) {
			throw new RuntimeException("加载驱动异常", e);
		} finally {
			if(is != null) {
				try {	is.close();	} catch (IOException e) {	throw new RuntimeException("关闭流异常", e);}
			}
		}
	}
	public static Connection getConnection() {
		Connection conn = null;
		try {
			conn = tdl.get();
			if(conn == null) {
				conn = DriverManager.getConnection(prop.getProperty("url"), prop.getProperty("user"), prop.getProperty("password"));
				tdl.set(conn);
			}
		} catch (SQLException e) {
			throw new RuntimeException("获得数据库连接异常", e);
		}
		return conn;
	}
	public static void close(ResultSet rs, PreparedStatement psmt, Connection conn) {
		if(rs != null) {
			try {	rs.close();	} catch (SQLException e) {throw new RuntimeException("关闭结果集异常", e);	}
		}
		if(psmt != null) {
			try {	psmt.close();} catch (SQLException e) {throw new RuntimeException("关闭psmt异常", e);}
		}
		if(conn != null) {
			try {
				conn.close();
				tdl.remove();
			} catch (SQLException e) {
				throw new RuntimeException("关闭数据库连接异常", e);
			}
		}
	}
}
数据访问层DAO代码:
public class AccountDAO { 
	public Account selectById(String cardId) {	//利用cardid查询
		Account account = new Account();
		Connection conn = null;
		ResultSet rs = null;
		PreparedStatement psmt = null;
		try {
			conn = JdbcUtil.getConnection();
			psmt = conn.prepareStatement("select * from t_account where cardid = ?");
			psmt.setString(1, cardId);
			rs = psmt.executeQuery();
			rs.next();
			account = new Account(rs.getString("cardid"), rs.getString("username"), rs.getString("password"), rs.getDouble("balance"));
		} catch (SQLException e) {
			throw new RuntimeException("查询数据库异常", e);
		} finally {
			JdbcUtil.close(rs, psmt, null); // Connection在Service层关闭 --原子性;
		}
		return account;
	}
	//查询所有
	public List<Account> selectAll() {
		List<Account> list = new CopyOnWriteArrayList<Account>();
		Connection conn = null;
		PreparedStatement psmt = null;
		ResultSet rs = null;
		try {
			conn = JdbcUtil.getConnection();
			psmt = conn.prepareStatement("select cardid, username, password, balance from t_account");
			rs = psmt.executeQuery();
			while(rs.next()) {
				list.add(new Account(rs.getString("cardid"), rs.getString("username"), rs.getString("password"), rs.getDouble("balance")));
			}
		} catch(Exception e) {
			throw new RuntimeException("查询所有信息异常", e);
		} finally {
			JdbcUtil.close(rs, psmt, null); //Connection在Service层关闭 --原子性
		}
		return list;
}
}
业务逻辑层Service代码:
public class AccountService {
	//查询所有账户
	public List<Account> findAll() {
		List<Account> list = new CopyOnWriteArrayList<Account>();
		Connection conn = null;
		try {
			conn = JdbcUtil.getConnection();
			conn.setAutoCommit(false);
			AccountDAO accountDAO = new AccountDAO();
			list = accountDAO.selectAll();
			conn.commit();
		} catch(Exception e) {
			try {	conn.rollback();} catch (SQLException e1) {throw new RuntimeException("回滚错误", e);}
			throw new RuntimeException("查询所有账户异常", e);
		} finally {
			JdbcUtil.close(null, null, conn);
		}
		return list;
	}
	//根据id查询 
	public Account findById(String cardId) {
		Account account = null;
		Connection conn = null;
		try {
			conn = JdbcUtil.getConnection();
			conn.setAutoCommit(false);
			AccountDAO accountDAO = new AccountDAO();
			account = accountDAO.selectById(cardId);
			conn.commit();
		} catch(Exception e) {
			try {	conn.rollback();} catch (SQLException e1) {throw new RuntimeException("回滚错误", e);}
			throw new RuntimeException("查询异常", e);
		}
		return account;
	}
}

项目实战3: (ThreadLocal底层)、编码规范、单元测试、连接优化(连接池)

1. Thread内部存储结构
   class Thread{
       ThreadLocalMap threadLocals   //Map类型的结构
   }

作用: 操作当前线程内部一块存储空间 (属性)
 借助当前线程Thread共享(传递)数据
创建: new ThreadLocal<泛型>()
① threadLocal.set(值); 将数据存入当前线程
	
② threadLocal.get(); 从当前线程中获取值

③ threadLocal.remove(); 从当前线程中移除值.

2. 编码规范
项目包结构规范:
包接口规范: 公司网址倒置: www.baizhiedu.com
com.baizhiedu.模块名.xxx
实体----entity
JdbcUtil----util
DAO接口----dao
DAO接口实现类----dao.impl
Service接口----service
Service实现类-----service.impl
view对象----view
配置文件----conf
sql文件----sql
测试代码----test
 编码步骤:
0. 导入jar  导入工具类   导入配置文件
1. 表(数据库设计)
2. 实体(映射数据库的表)
3. 开发UserDAO接口:  XxxDAO
4. 开发UserDAO实现类:  XxxxDAOImpl
5. 开发UserService接口:  XxxxService
6. 开发UserService实现类:  XxxxServiceImpl
7. 开发View
接口定义:
    作用:
 		1. 对dao和service定义标准.提高代码可维护性.
 		2. 更加符合程序员代码开发思路,先宏观(类 方法声明),再微观(方法实现)
 	DAO开发先写接口.
 	调用: XxxDAO dao = new XxxDAOImpl()
 		 dao.xxx();
 	Service开发先写接口,
	调用: XxxService service = new XxxServiceImpl()
		 service.xxx();
3. 单元测试
作用: 代码中每个方法单元,书写完毕都要通过测试验证方法可用性.
Junit测试工具
使用:
1. 声明一个方法(没有参数,没有返回值) 
2. 测试方法上添加`@Test`
3. 通过junit启动
4. 连接优化(连接池)
-连接池: 接口 DataSource (数据源)
-连接处理的问题:
①用户执行代码需要conn,需要等待连接的创建
②用户操作完毕,conn.close,宝贵(昂贵)资源,直接销毁.
-解决思路:
①事先为用户准备一定数量的conn,当需要使用conn,直接从区域(连接池)获取conn(免去conn创建需要等待的时间)
②conn使用完毕之后,还回到存放conn的区域(连接池)(昂贵的conn资源,重复利用)
-作用: 事先创建一定数量的连接放入连接池,使用只接获取,无需等待.
-常见连接池:  dbcp c3p0   tomcat-pool   阿里Druid
-阿里druid连接池(使用):
导入druid相关的jar
①创建Datasource(连接池)
   	DataSource ds = DruidDatasourceFactory.createDatasource(包含了连接池配置信息的Properties);
②DataSource中获得连接
Connection conn = ds.getConnection();//只接获得连接池中的conn
		修改以上的JdbcUtil工具类代码:
配置文件conf.properties代码:
	driverClassName=oracle.jdbc.OracleDriver
url=jdbc\:oracle\:thin\:@localhost\:1521\:orcl
user=hr
password=Oracle123
initialSize=10;
maxActive=50;
maxWait=6000;
工具类JdbcUtil代码:
package com.baizhi.util;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class JdbcUtil4 {
	private static final Properties props = new Properties();
	private static final ThreadLocal<Connection> tdl = new ThreadLocal<Connection>();
	private static DataSource ds = null;
	static {
		InputStream is = null;  // 1. 输入流
		try {
			is = JdbcUtil4.class.getResourceAsStream("/com/baizhiedu/user/conf/druid.properties");
			props.load(is);  // 2. load方法加载配置文件--- props存放连接池相关配置信息
			ds = DruidDataSourceFactory.createDataSource(props);  //3. 创建连接池
		} catch (Exception e) {
			throw new RuntimeException("配置文件加载异常!", e);
		} finally {
			if (is != null) {
				try {	is.close();	} 
catch (IOException e) {e.printStackTrace();}
			}
		}
	}
//获得数据库连接
	public static Connection getConnection() {
		Connection conn = null;
		try { 	//从当前线程获得conn
			conn = tdl.get();
			if(conn == null){
				conn = ds.getConnection();  //从连接池获得连接
				tdl.set(conn);   //将连接存入当前线程
			}
		}catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("获得数据库连接异常",e);
		}
		return conn;
	}
	//释放资源: 关闭 rs pstm conn   
	public static void close(ResultSet rs, PreparedStatement pstm,Connection conn) {
		// 对rs pstm conn
		if (rs != null) {
			try {	rs.close();	} 
catch (SQLException e) {	throw new RuntimeException("结果集资源释放异常", e);}
		}
		if (pstm != null) {
			try {	pstm.close();} 
catch (SQLException e) {	throw new RuntimeException("pstm资源释放异常", e);}
		}
		if (conn != null) {
			try {	conn.close();//将conn从当前线程移除,是复写的close方法
				tdl.remove();
			} catch (SQLException e) {throw new RuntimeException("连接资源释放异常", e);}
		}
	}
}
连接池:补充说明
1. 连接返回给连接池.
   	conn.close()
说明: 调用的连接池封装的Connection对象的close方法,方法内部操作未断开连接,将内部conn放回到连接池.
2. 连接池内部的连接类设计
   	从连接池中获得conn是连接池自己封装的conn类.
  	 // 代理设计模式
   	public class DruidConnection implements Connection{
   	    private Connection oracleConnection = null;
   	    public preparedStatement(){}
  	    public void close(){
          	DataSource.recycle(oracleConnection);
       	}
   	}
   	public class DruidDataSource implements DataSource{
       	private List<Connection> conns;
   	}
posted @ 2020-09-08 19:42  JWnMing  阅读(139)  评论(0编辑  收藏  举报