项目实战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;
}