【javaweb简单教程】4.DAO模式及单例模式(封装jdbc、用配置文件访问数据库、单例模式)(含完整代码)

本文详细介绍了在Java中如何使用DAO模式进行JDBC的封装,以降低业务代码与数据访问代码的耦合,提高代码可读性和可维护性。同时,通过实例展示了如何运用单例模式优化数据库连接工具类,确保系统资源的有效利用。单例模式分为懒汉模式和饿汉模式,前者在类加载时不创建实例,后者在类加载时即完成初始化。在实际应用中,可以根据需求选择适合的单例实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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();
			}
		}
		
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tuerlechat,

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值