1 Dao设计思想
Java语言要操作数据库表里面的数据时,要使用jdbc,但是jdbc的代码非常的繁琐,如果经常操作数据的话,这些代码的重复度会很高。所以我们把经常对表的相关操作封装到我们的dao类。这样就提高了代码的利用率,提高了系统的可维护性,可扩展性。
Dao(data access object)数据访问对象。
Dao里面需要有以下几个组件:
1.Person类。实体类。
a) 这个类跟表是一一对应的。
b) 他里面的属性跟表里面的字段(列)是一一对应的。
c) 属性私有,要有get和set方法。以及构造方法。
d) 实现序列化接口。
2.PersonDao类。Dao类
a) 里面五个方法,增删改查。
b) Void insert(Person p)
c) Void delete(int id)
d) Vodi update(Person p)
e) List selectAll()
f) Person selectOne(int id)
3.Test类测试类
a) 调用dao方法,对dao方法进行测试。
Dao的作用:
1.提高代码的可利用率
2.让各个组件功能更加单一,满足单一职能原则。
下面是一个Dao类的增删改查方法:
package jdb import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class PersonDao { /* * 到数据库表里面把p插入进去 */ public void insert(Person p){ // System.out.println(p.getId()+"\t"+p.getName()); Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try{ conn=JdbcUtils.getConnection(); //3.写sql String sql="insert into person values(person_seq.nextval,?,?,?,?)"; //4.创建ps ps=conn.prepareStatement(sql); //5.给占位符赋值 ps.setString(1,p.getName()); ps.setInt(2,p.getAge()); ps.setString(3,p.getTel()); ps.setString(4,p.getAddress()); //6.执行 ps.executeUpdate(); }catch(Exception e){ e.printStackTrace(); }finally{ JdbcUtils.closeAll(conn,ps,rs); } } /** * 根据id把数据库表里面对应的数据删除掉 * @param id 要删除的数据的编号 */ public void delete(int id){ Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try{ conn=JdbcUtils.getConnection(); //3.写sql String sql="delete from person where id=?"; //4.创建ps ps=conn.prepareStatement(sql); //5.给占位符赋值 ps.setInt(1,id); //6.执行 ps.executeUpdate(); }catch(Exception e){ e.printStackTrace(); }finally{ JdbcUtils.closeAll(conn, ps, rs); } } public void update(Person p){ Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try{ conn=JdbcUtils.getConnection(); //3.写sql String sql="update person set name=?,age=?,tel=?,address=? where id=?"; //4.创建ps ps=conn.prepareStatement(sql); //5.给占位符赋值 ps.setString(1,p.getName()); ps.setInt(2,p.getAge()); ps.setString(3,p.getTel()); ps.setString(4,p.getAddress()); ps.setInt(5,p.getId()); //6.执行 ps.executeUpdate(); }catch(Exception e){ e.printStackTrace(); }finally{ JdbcUtils.closeAll(conn,ps,rs); } } public Person selectOne(int id){ Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; Person p=null; try{ conn=JdbcUtils.getConnection(); //3.写sql String sql="select * from person where id=?"; //4.创建ps ps=conn.prepareStatement(sql); //5.执行 ps.setInt(1, id); rs=ps.executeQuery(); if(rs.next()){ //获取表中每一个字段的值 String name=rs.getString("name"); int age=rs.getInt("age"); String tel=rs.getString("tel"); String address=rs.getString("address"); //把上面这些数据封装到person对象里面 p=new Person(id,name,age,tel,address); } }catch(Exception e){ e.printStackTrace(); }finally{ JdbcUtils.closeAll(conn,ps,rs); } return p; } /* * 到数据库里面把所有的person实体查询出来,封装到list集合,然后返回该list集合。 */ public List<Person> selectAll(){ Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; List<Person> perList=new ArrayList<Person>(); try{ conn=JdbcUtils.getConnection(); //3.写sql String sql="select * from person"; //4.创建ps ps=conn.prepareStatement(sql); //5.执行 rs=ps.executeQuery(); while(rs.next()){ //获取表中每一个字段的值 int id=rs.getInt("id"); String name=rs.getString("name"); int age=rs.getInt("age"); String tel=rs.getString("tel"); String address=rs.getString("address"); //把上面这些数据封装到person对象里面 Person p=new Person(id,name,age,tel,address); //把person放入list集合 perList.add(p); } }catch(Exception e){ e.printStackTrace(); }finally{ JdbcUtils.closeAll(conn,ps,rs); } return perList; } }
main, Test测试类:
package jdbc; import java.util.List; public class Test { public static void main(String[] args) { //实例化对象,才能调用该类里面的方法。 PersonDao pd=new PersonDao(); /*//调用insert方法时,需要的实参 Person p=new Person(1,"zhangsan",20,"152226565","郑州市"); Person p2=new Person("lisi",20,"152226565","郑州市"); //调用pd对象的insert方法。 pd.insert(p);*/ // List<Person> perList=pd.selectAll(); // for(Person p:perList){ // System.out.println(p.getName()+"====="+p.getAge()); // } // pd.delete(76); // Person p=new Person(77,"wangwu",21,"152226566","郑州市"); // pd.update(p); Person p=pd.selectOne(77); System.out.println(p.getName()+"\t"+p.getAge()); } }
上面代码存在的问题:
1.dao类中的增删改查方法里面的代码的重复度非常高
2.每个方法开始都是要建立数据库连接
3.每个方法的结尾都是要释放资源。
4.我们要去除代码的重复度。
解决办法:
在dao类抽取两个方法。
Public Connection getConnection();
Public void closeAll(Connection conn,PreparedStatement ps,ResultSet rs);
这样,增删改查方法就可以重用上面两个方法的代码。
代码改进后又有新的问题:
1.后期项目复杂后,一个项目中对应多个表。
2.多个表就会有多个dao类
3.每个dao类中都需要数据库连接和释放资源。
4.因为刚才我们把建立连接和释放资源的代码封装到了PersonDao。那么其他的dao类调用这两个方法就非常麻烦。
2 JdbcUtils类
为了解决上面的问题,我们可以把建立连接和释放资源这两个方法封装到一个公共的工具类中JdbcUtils.这个类为所有dao类服务。
因为getConnection方法和closeAll跟JdbcUtils的对象没有关系。我们就把这两个方法定义成静态方法。这样dao类在使用这个两个方法时,就可以直接使用类名调用,而不用创建JdbcUtils对象了。
Dao类做增删改查操作,JdbcUtils类和实体类协助dao。
3 在JdbcUtils类里面使用Properties对象
虽然抽出来两个方法使得我们在开发的时候代码的重复度降低,但是现在项目还存在一个问题,就是加载驱动和创建数据库连接都是我们直接写死的,但是这些字符串常量是有可能要修改的。在真实运行环境中是没有源码的。没有源码就不能修改JdbcUtils类。
所以我们就可以把这些字符串常量抽取出来存放在一个文件中,然后每次读取的时候从这个文件中读取数据,这样就很好的解决了可以单独修改连接条件。
java Jdk中有一个类叫Properties。
1.他本身是一个map集合,可以存储数据。
2.他有load(Reader r);方法,该方法可以自动加载文本文件中的数据。
3.要使用文本文件中的数据时,可以调用getProperties(String)获取文本文件中的数据。
JdbcUtil类:
package com.macw.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; /** * @author 超伟 * @2019年5月28日 下午1:51:58 * @博客:https://blog.csdn.net/MacWx */ public class JdbcUtil { public static final Properties prop = new Properties(); //把流操作提取到静态代码块里面。 static{ InputStream in = null; //使用类加载读取jdbc.properties文件 in = JdbcUtil.class.getResourceAsStream("/jdbc.properties"); try { //读取文件中的数据 prop.load(in); Class.forName(prop.getProperty("DriverClassName")); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e); }finally{ if (in!=null) { try { in.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * @return Connection */ public static Connection getConnection(){ Connection conn = null; try { conn = DriverManager.getConnection(prop.getProperty("url"),prop.getProperty("username"),prop.getProperty("password")); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e); } return conn; } /** * * @param conn * @param ps * @param rs */ public static void toClose(Connection conn,PreparedStatement ps,ResultSet rs){ try { if (rs!=null) { rs.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { if(ps!=null){ ps.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { if (conn!=null) { conn.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void toClose(Connection conn,PreparedStatement ps){ try { if(ps!=null){ ps.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { if (conn!=null) { conn.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
优化JdbcUtils类
1.在src里面创建一个jdbc.properties文件。在src右键—》new—》file—>jdbc.propertis
2.在这个文件中去写键值对,键值之间用=隔开
DriverClassName=oracle.jdbc.OracleDriver url=jdbc:oracle:thin:@localhost:1521:xe username=hr password=mcw
4 数据库连接池技术
为什么要使用数据库连接池:
1.频繁建立连接和释放连接非常浪费资源。
2.可以把一些连接存储起来,有人需要使用时,就从里面获取空闲连接
怎么使用数据库连接池?
C3P0,DBCP,DRUID,JDNI
DRUID是阿里实现的一种数据库连接池技术。
1.导入druid的相关jar包
2.将druid内部使用的字符串常量配置到properties配置文件中。
3.在代码中使用数据库连接池。
a) 创建出数据库连接池对象。(静态代码块)
b) 从数据库连接池对象里面获取连接。(getConnection())
c) 使用完后,把数据库连接归还给连接池。(closeAll())
可以参考JdbcUtils工具类里面的代码。