4.DAO模式及单例模式
JDBC封装
原因
- 业务代码和数据访问代码耦合
- 可读性差
- 不利于后期修改和维护
- 不利于代码复用
- 注意:
- 将相似功能的代码抽取封装成方法,减少代码冗余
- 因为不同的数据库会有不同的实现,对数据库的操作一般抽取成接口,在以后的开发中可以降低耦合
实现
- 将所有增删改查操作抽取成接口
- 定义实体类传输数据
- 将通用的操作(打开、关闭连接等)封装到工具类
- 数据库工具类BaseDao:增、删、改、查的通用方法
DAO
- Data Access Object(数据存取对象)
- 位于业务逻辑和持久化数据之间
- 实现对持久化数据的访问
DAO起着转换器的作用,将数据在实体类和数据库记录之间进行转换
DAO模式的组成
- DAO模式的组成部分
- DAO接口
- DAO实现类
- 实体类
- 数据库连接和关闭工具类
- 优势
- 隔离了数据访问代码和业务逻辑代码
- 隔离了不同数据库实现
示例
dao接口:
/**
* 学生dao接口
* @author
*
*/
public interface StudentDao {
//查询所有学生信息
public List<Student> queryAll();
//登录后查询所有学生信息
public List<Student> login(String stuname, String stupwd);
//添加学生信息
public int addInfo(String stupwd,String stuname,Integer gradeid,String borndate);
}
dao实现类:
/**
* 学生dao实现类
* @author
*
*/
public class StudentDaoImpl extends BaseDao1 implements StudentDao { //继承BaseDao或BaseDao1类,获得封装jdbc的连接,然后实现StudentDao接口
List<Student> students = new ArrayList<>();
/**
* 查询所有学生数据
* @return
*/
@Override
public List<Student> queryAll() {
try {
if (getConnection()) {
String sql = "select * from student a \r\n"
+ "left JOIN grade b on a.GradeId = b.GradeID";
rs = executeSQL(sql, null);
while(rs.next()) {
Student stu = new Student();
stu.setStudentNo(rs.getInt("StudentNo"));
stu.setStudentName(rs.getString("StudentName"));
stu.setBornDate(rs.getString("BornDate"));
Grade grade = new Grade();
grade.setGradeName(rs.getString("GradeName"));
stu.setGrade(grade);
students.add(stu);
}
return students;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
closeResources();
}
return null;
}
/**
* 登录后查询所有学生信息
*/
@Override
public List<Student> login(String stuname, String stupwd) {
try {
if (getConnection()) {
String sql = "select * from student where studentname = ? and loginpwd = ?";
rs = executeSQL(sql, new Object[] {stuname,stupwd});
Student stu = new Student();
if(rs.next()) {
if (stu != null) {
System.out.println(stuname + ",登录成功。");
//调用queryAll()方法
List<Student> list = queryAll();
return list;
}
} else {
System.out.println("登录失败。");
}
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
closeResources();
}
return null;
}
/**
* 添加学生信息
*/
@Override
public int addInfo(String stupwd,String stuname,Integer gradeid,String borndate) {
try {
if (getConnection()) {
String sql = "insert into student (LoginPwd,StudentName,GradeId,BornDate) values (?,?,?,?)";
int n = executUpdate(sql, new Object[] {stupwd,stuname,gradeid,borndate});
return n;
}
} catch (Exception e) {
e.printStackTrace();
return 0;
} finally {
closeResources();
}
return 0;
}
}
配置数据库访问参数
- 弊端
- 数据库发生改变时,要重新修改代码,重新编译和部署
- 解决
- 将数据库信息写在配置文件当中,让程序通过读取配置文件来获得这些信息
属性文件
- 后缀为
.properties
- 数据格式为“
键=值
”- 使用“
#
”来注释- Java中提供了Properties类来读取配置文件
示例
jdbc.properties代码:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
username=用户名
userpwd=密码
BaseDao封装类代码:
1、用静态代码块实现:
/**
* 数据库连接工具类
* @author
*
*/
public class BaseDao {
protected ResultSet rs = null;
protected PreparedStatement ps = null;
protected Connection conn = null;
static Properties pro = null;
//静态代码块
static {
pro = new Properties();
InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
pro.load(is);
} catch (IOException e) {
e.printStackTrace();
}
}
//获得数据库连接
public boolean getConnection() {
try {
//加载驱动
Class.forName(pro.getProperty("driver"));
//准备URL 连接数据库
String url = pro.getProperty("url");
//准备账号密码
String uname = pro.getProperty("username");
String upwd = pro.getProperty("userpwd");
//通过DriverManager.getConnection获得Connection对象
conn = DriverManager.
getConnection(url, uname, upwd);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 公共修改 添加 删除
* @param sql
* @param objs
* @return
*/
public int executUpdate(String sql,Object[] objs) {
int updateRows = 0;
try {
ps = conn.prepareStatement(sql);
if(objs!=null) {
//填充占位符
for(int i = 0;i<objs.length;i++)
{
ps.setObject((i+1), objs[i]);
}
}
updateRows = ps.executeUpdate();
return updateRows;
} catch (SQLException e) {
e.printStackTrace();
return updateRows;
}
}
/**
* 公共查询
* @param sql 携带占位符 ?
* @param objs 参数数组
* @return
*/
public ResultSet executeSQL(String sql,Object[] objs) {
try {
ps = conn.prepareStatement(sql);
if(objs!=null) {
//填充占位符
for(int i = 0;i<objs.length;i++)
{
ps.setObject((i+1), objs[i]);
}
}
rs = ps.executeQuery();
return rs;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
/**
* 释放资源
* @return
*/
public void closeResources() {
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ps!=null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
单例模式
使用原因
- BaseDao:操作数据库的基类
- 每个线程对系统操作都需new一个BaseDao实例
- 初始化时的I/O操作消耗系统资源,影响系统性能
对于每个线程,可共享一个实例
实现
- 系统运行期间,有且仅有一个实例
- 一个类只有一个实例——最基本的要求
- 只提供私有构造器
- 它必须自行创建这个实例
- 定义了静态的该类私有对象
- 它必须自行向整个系统提供这个实例
- 提供一个静态的公有方法,返回创建或者获取本身的静态私有对象
懒汉模式
- 在类加载时不创建实例,采用延迟加载的方式,在运行调用时创建实例
- 特点
- 线程不安全
- 延迟加载(lazy loading)
- 如何解决线程安全问题?
- 同步(synchronized)
饿汉模式
- 在类加载的时候,就完成初始化
- 特点
- 线程安全
- 不具备延迟加载特性
小结
注意: 在整个程序运行期间,有且仅有一个实例。若违背这一点,所设计的类就不是单例类
单例模式 | 懒汉模式 | 饿汉模式 |
---|---|---|
概念 | 在类加载时不创建实例,采用延迟加载的方式,在运行调用时创建实例 | 在类加载的时候,就完成初始化 |
特点 | 类加载速度快,但是运行时获取对象的速度较慢。——“时间换空间” | 类加载较慢,但获取对象速度快。——“空间换时间” |
延迟加载 (lazy loa ding) | 具备 | 不具备 |
线程安全 | 线程不安全 | 线程安全 |
示例(续)
2、用单例模式实现:
ConfigTools类代码:
/**
* 封装数据库连接工具类(单例饿汉模式)
* @author
*
*/
public class ConfigTools {
/**
*
* @param args
*/
static Properties pro = null;
static ConfigTools ct = new ConfigTools();
public ConfigTools() {
pro = new Properties();
InputStream is = ConfigTools.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
pro.load(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Properties getProperties() {
return pro;
}
//更懒一步,后面basedao文件参数也跟着改改
// public static String getProperties(String key) {
// return pro.getProperty(key);
// }
public static void main(String[] args) {
System.out.println(pro.getProperty("url"));
}
}
basedao1封装类代码:
/**
* 数据库连接工具类
* @author zy
*
*/
public class BaseDao1 {
protected ResultSet rs = null;
protected PreparedStatement ps = null;
protected Connection conn = null;
public static void main(String[] args) {
System.out.println();
}
//获得数据库连接
public boolean getConnection() {
try {
//加载驱动
Class.forName(ConfigTools.getProperties().getProperty("driver"));
//准备URL 连接数据库
String url = ConfigTools.getProperties().getProperty("url");
//准备账号密码
String uname = ConfigTools.getProperties().getProperty("username");
String upwd = ConfigTools.getProperties().getProperty("s");
//懒省事修改后的的参数
// //加载驱动
// Class.forName(ConfigTools.getProperties("driver"));
// //准备URL 连接数据库
// String url = ConfigTools.getProperties("url");
// //准备账号密码
// String uname = ConfigTools.getProperties("username");
// String upwd = ConfigTools.getProperties("userpwd");
//通过DriverManager.getConnection获得Connection对象
conn = DriverManager.
getConnection(url, uname, upwd);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 公共修改 添加 删除
* @param sql
* @param objs
* @return
*/
public int executUpdate(String sql,Object[] objs) {
int updateRows = 0;
try {
ps = conn.prepareStatement(sql);
if(objs!=null) {
//填充占位符
for(int i = 0;i<objs.length;i++)
{
ps.setObject((i+1), objs[i]);
}
}
updateRows = ps.executeUpdate();
return updateRows;
} catch (SQLException e) {
e.printStackTrace();
return updateRows;
}
}
/**
* 公共查询
* @param sql 携带占位符 ?
* @param objs 参数数组
* @return
*/
public ResultSet executeSQL(String sql,Object[] objs) {
try {
ps = conn.prepareStatement(sql);
if(objs!=null) {
//填充占位符
for(int i = 0;i<objs.length;i++)
{
ps.setObject((i+1), objs[i]);
}
}
rs = ps.executeQuery();
return rs;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
/**
* 释放资源
* @return
*/
public void closeResources() {
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ps!=null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}