DAO:Data Access Object,数据访问对象,主要的功能就是进行数据库数据的操作,模拟出企业的开发架构:
- 客户层:浏览器
- 显示层:使用jsp/Servlet/html进行页面效果显示
- 控制层:使用Servlet完成,是连接显示层和业务层
- 业务层:service层,具体进行业务处理,是连接控制层和持久化层,并且进行一些业务的处理,比如事务等
- 持久化层:也叫作数据访问层,JDBC完成
- 数据库:MySQL/Oracle
- 以后的开发中,在jsp页面中,是绝对不允许再出现导入java.sql包的;
一个DAO实际上就是一个JavaBean的组件模型。
DAO:DataBase Access Object,数据库访问对象,数据库表(或视图)中的每一笔数据就是实体类的一个对象,实体类中的属性就是数据表中的字段,这个类我们就叫做实体类,也叫作对象关系映射类(这个类的每一个对象都可以映射到数据表的一笔数据,ORM)。
DAO中会使用到的其他的设计模式有静态工厂模式和代理模式。DAO的组成:
1.实体类(entities):数据库表或视图中的每一笔数据就是实体类的一个对象,一个表或视图就是一个实体类,实体类的命名使用表名的大驼峰命名。属性名使用小驼峰命名。
2.Dao接口:定义一组数据库表增删改查的操作,只需要声明,不需要实现,接口名称的命名规则:实体类名Dao
接口中方法的命名规则
- 新增 : insert 或者 add
- 修改 : update 或者updateByXxx
- 删除:delete 或者deleteByXxx
- 查询:
- getXxxByXxx或者findXxxByXxx,比如根据名称查询学生信息: getStudentInfoByStuName/findStudentInfoByStuName
- 查询 : selectXxxByXxx或者getXxxByXxx,比如我们根据学生名称模糊查询学生信息(selectStudentByStuNameLike(String stuName))
- 根据书号查询图书的信息 getBooksByBookId
- 根据图书名称模糊查询图书信息:getBooksByBookNameLike
3.Dao接口的实现类:真实主题类,实现Dao接口中所有声明的方法,具体去完成增删改查操作,但是不关注数据库连接的取得和关闭,Dao接口实现类命名规则:Dao接口名Imp |Dao接口名Impl
33 代理实现类[丢弃]:实现Dao接口,代理真实主题类完成数据库连接的取得和关闭,并且调用真实主题类,命名规则:Dao名称Proxy
[因为开发需要使用事务技术,而事务又是在service层进行操作,所以数据库连接的取得和关闭都放在业务层]
4.Dao工厂类:有接口就需要使用工厂,工厂类的作用是用来完成接口对象生产,以便于service层的调用。工厂的作用就是返回Dao的实例对象。
生产Dao接口对象的,一般使用静态工厂方法生产Dao对象,工厂类的命名规则:
Dao接口名Factory,静态工厂方法名:get接口名Instance,静态方法中return返回接口的对象。
总结:
Dao层包括,实体类,Dao接口,Dao接口实现类,Dao接口的代理实现类(取得数据库的连接和关闭),工厂类(生产接口对象)
每一张表或者一个视图都会存在一个Dao。之前数据库的连接和关闭放在代理实现类,加了事务后数据库的连接和关闭放在业务实现类层。
数据库表中的一笔数据,就是实体类的一个对象,实体类中的属性就是数据表中的字段。
DAO设计模式(数据访问对象),并不是java23种设计模式的一种,因为它在所有语言都可以使用,DAO设计模式需要使用到静态工厂和代理模式这两种模式拼凑在一起。
工厂方法模式:实现类与类之间充分的解耦合操作,比如我们之前在类中直接实例化另外一个类的对象,我们说这个耦合度就很高,导致程序的可维护性降低。
A类:
B类:需要实例化A类对象。
直接调用A类构造器,需要知道A类构造器要什么参数,要知道A类构造器如何编写的,然后通过new去创造这个对象,这种方式对B类要求很高,相当于需要一个斧头,必须知道这个斧头的制造过程,才能拥有这把斧头。
这是原始社会用斧头操作,自己做斧头。封建社会要用斧头去铁匠铺买斧头,去买斧头,这个铁匠铺就相当于生产斧头的工厂。
工厂方法模式就是,去搞一个生产对象的工厂,B类要拿到A类对象,不需要完全的了解A类到底有什么样的构造器,要创造它的对象,而只需要去工厂拿,调用工厂的方法去拿就行了。
使用工厂方法之后,相当于从原始社会过渡到了封建社会,在原始社会需要一个对象,必须知道这个对象所对应的类型中构造器是如何定义的,如何传递参数的,才能够获取到想要的类的对象,在封建社会,我们不关注对象的产生,而是将对象产生的过程交给工厂去完成,需要这个对象的地方只要调用工厂方法就可以获取想要的对象。
静态工厂:工厂类中提供的获取对象的方法是静态的。(用的多)
实例工厂:工厂类中提供的获取对象的方法是非静态的。
代理模式:要完成一个操作,主要的内容是A完成的,有一部分是B帮助A完成的,这个时候B就是代理A完成了一些工作。
一个接口两个类,A是真实主题类,B是代理类。真实主题类和代理类都需要去实现接口,而且需要在代理类中获取真实主题类的对象,并且调用真实主题类中的方法,就是在B类中获取A类的对象,并且调用A中的方法。
1.控制层:原生开发:Servlet,框架开发: Struts2(Action)SpringMVC(Handler)
2.业务层:service层,处理实际的业务,调用一个或者多个Dao,一个业务就是一个service
Service层包括,Service接口和接口实现类。
3.数据访问层(持久化层):Dao层,完成数据库实际的增删改查操作
Dao层包括,实体类,Dao接口,Dao接口实现类,工厂类(生产接口对象)
总结:先来三个包名,com.wanbangee.dao com.wanbangee.service com.wanbangee.controller
在dao包中先建entities实体类(一个表或视图就是一个实体类,set和get方法,有参和无参构造器),dao , daoimp, daoproxy(丢弃), daofactory
数据库(book)有三张表:
图书信息表(books,有3个字段,book_id,book_name,price),
图书库存表(book_stock,有3个字段,book_id,book_name,stock),
账户余额表(account,有3个字段,loginname,loginpass,balance),
需要完成的买书业务,买书的过程:1 查询图书单价。2 减少账户余额。3 减少图书库存
对于增删改来说,一定要进行事务的管理。
这三个操作要么全部成功要么全部失败,所以必须加事务,而且事务应该加在业务层。
注意:异常的处理放在业务层,如果在数据库访问层(Dao层)处理异常,那么业务层的事务将会失效。
实体类:
package com.wanbangee.entities;
public class Account {
private String loginname;
private String loginpass;
private double balance;
public String getLoginname() {
return loginname;
}
public void setLoginname(String loginname) {
this.loginname = loginname;
}
public String getLoginpass() {
return loginpass;
}
public void setLoginpass(String loginpass) {
this.loginpass = loginpass;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public Account(String loginname, String loginpass, double balance) {
super();
this.loginname = loginname;
this.loginpass = loginpass;
this.balance = balance;
}
public Account() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Account [loginname=" + loginname + ", loginpass=" + loginpass + ", balance=" + balance + "]";
}
}
package com.wanbangee.entities;
public class Books {
private int bookId;
private String bookName;
private double price;
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Books(int bookId, String bookName, double price) {
super();
this.bookId = bookId;
this.bookName = bookName;
this.price = price;
}
public Books() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Books [bookId=" + bookId + ", bookName=" + bookName + ", price=" + price + "]";
}
}
package com.wanbangee.entities;
public class BookStock {
private int bookId;
private String bookName;
private int stock;
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public BookStock(int bookId, String bookName, int stock) {
super();
this.bookId = bookId;
this.bookName = bookName;
this.stock = stock;
}
public BookStock() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "BookStock [bookId=" + bookId + ", bookName=" + bookName + ", stock=" + stock + "]";
}
}
Dao接口:
package com.wanbangee.dao;
public interface AccountDao {
//谁买书谁减少余额,根据买书人名字减少余额,减少的余额就是所买书的单价,一次买一本书
public void doUpdate(String loginname,double price);
}
package com.wanbangee.dao;
public interface BooksDao {
//根据图书ID查询图书价格
public Double getPriceByBookId(int bookId);
}
package com.wanbangee.dao;
public interface BookStockDao {
//减少所买图书库存
public void doUpdate(int bookId);
}
Dao接口实现类:
package com.wanbangee.dao.imp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.wanbangee.dao.AccountDao;
public class AccountDaoImp implements AccountDao {
/*真实主题类,实现Dao接口中所有声明的方法,
具体去完成增删改查操作,但是不关注数据库连接的取得和关闭*/
//1先定义数据库连接对象,但这里不连接
Connection conn ;
//现在没有连接数据库,这个数据库连接对象conn是空的,空的要是执行增删改查会报错
//谁调我谁负责把连接拿过来,谁调我,谁要去实例化我这个对象,就把数据库连接对象conn拿过来
//谁要使用接口实现类的对象就必须将数据库连接对象conn传递进来
//接口实现类本身没有去获取数据库连接,只是谁调用我谁给我
public AccountDaoImp(Connection conn) {
this.conn = conn;
}
//减少账户余额,是个修改操作
@Override
public void doUpdate(String loginname, double price) {
PreparedStatement pstate = null;
try {
String sql = "update account set balance = balance - ? where loginname = ? ";
//pstate = conn.prepareStatement(sql);
pstate = this.conn.prepareStatement(sql);
pstate.setDouble(1, price);
pstate.setString(2, loginname);
pstate.execute();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
pstate.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package com.wanbangee.dao.imp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.wanbangee.dao.BooksDao;
public class BooksDaoImp implements BooksDao {
Connection conn;
public BooksDaoImp(Connection conn) {
this.conn = conn;
}
//查询图书的单价,是个查询操作
@Override
public Double getPriceByBookId(int bookId) {
PreparedStatement pstate = null;
ResultSet res = null;
double price = 0;//定义图书价格,给个默认值0
try {
String sql = "select price from books where book_id = ?";
//pstate = conn.prepareStatement(sql);
pstate = this.conn.prepareStatement(sql);
pstate.setInt(1, bookId);
res = pstate.executeQuery();
while(res.next()) {
price = res.getDouble("price");
//获得这一本的书价赋值给double price = 0;中的price变量,最后return返回price这个变量
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
res.close();
pstate.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return price;//调用这个方法返回图书价格
}
}
package com.wanbangee.dao.imp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.wanbangee.dao.BookStockDao;
public class BookStockDaoImp implements BookStockDao {
Connection conn;//谁调我谁传数据库连接对象conn进来
public BookStockDaoImp(Connection conn) {
this.conn = conn;
}
//每个接口实现类都是完成自己的业务,其它的代码都一样
@Override//修改图书库存,是个修改操作,只需预处理对象,不用查询结果集
public void doUpdate(int bookId) { //一次买一本书
PreparedStatement pstate = null;
try {
String sql = "update book_stock set stock = stock -1 where book_id = ?";
//pstate = conn.prepareStatement(sql);
pstate = this.conn.prepareStatement(sql);
pstate.setInt(1, bookId);
pstate.execute();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
pstate.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Dao工厂类:
package com.wanbangee.dao.factory;
import java.sql.Connection;
import com.wanbangee.dao.AccountDao;
import com.wanbangee.dao.imp.AccountDaoImp;
public class AccountDaoFactory {
public static AccountDao getAccountDaoInstance(Connection conn) {
return new AccountDaoImp(conn);
}
}
package com.wanbangee.dao.factory;
import java.sql.Connection;
import com.wanbangee.dao.BooksDao;
import com.wanbangee.dao.imp.BooksDaoImp;
public class BooksDaoFactory {
public static BooksDao getBooksDaoFactory(Connection conn) {
return new BooksDaoImp(conn);
}
}
package com.wanbangee.dao.factory;
import java.sql.Connection;
import com.wanbangee.dao.BookStockDao;
import com.wanbangee.dao.imp.BookStockDaoImp;
public class BookStockDaoFactory {
public static BookStockDao getBookStockDaoInstance(Connection conn) {
return new BookStockDaoImp(conn);
}
}
业务层service接口:
package com.wanbangee.service;
public interface BuyBookService {
//买一本书,谁买哪本书
public boolean buyBook(String loginname,int bookId);
}
业务实现类:数据库的连接和关闭放在业务实现类层
package com.wanbangee.service.imp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.wanbangee.dao.factory.AccountDaoFactory;
import com.wanbangee.dao.factory.BookStockDaoFactory;
import com.wanbangee.dao.factory.BooksDaoFactory;
import com.wanbangee.jdbc.DBUtil;
import com.wanbangee.service.BuyBookService;
public class BuyBookServiceImp implements BuyBookService {
//数据库的连接和关闭放在业务层
@Override
public boolean buyBook(String loginname, int bookId) {
boolean flag = false;
Connection conn = null;
PreparedStatement pstate = null;
ResultSet res = null;
/*买书的过程:
1 查询图书的单价
2 减少账户余额
3 减少图书库存
这三个操作要么全部成功要么全部失败,所以必须加事务,而且事务应该加在业务层。*/
try {
conn = DBUtil.getConnection();
//1.取消事务的自动提交
conn.setAutoCommit(false);
//1 查询图书的单价,单价在BooksDao,
double price = BooksDaoFactory.getBooksFactoryInstance(conn).getPriceByBookId(bookId);
//工厂类调用静态工厂方法获得接口对象,对象调用接口中的方法
//2 减少账户余额
AccountDaoFactory.getAccountDaoInstance(conn).doUpdate(loginname, price);
//3 减少图书库存
BookStockDaoFactory.getBookStockDaoInstance(conn).doUpdate(bookId);
String sql = "select balance from account where loginname = ? ";
pstate = conn.prepareStatement(sql);
pstate.setString(1, loginname);
res = pstate.executeQuery();
while(res.next()) {
if(res.getDouble("balance") < 0) {
System.out.println(res.getDouble(1));
throw new RuntimeException("余额不足");
}else{
System.out.println("余额够"+"==="+res.getDouble(1));
}
}
String sql2 = "select stock from book_stock where book_id = ?";
pstate = conn.prepareStatement(sql2);
pstate.setInt(1, bookId);
res = pstate.executeQuery();
while(res.next()) {
if(res.getInt("stock") < 0) {
System.out.println(res.getInt(1));
throw new RuntimeException("库存不足");
}else {
System.out.println("库存够"+"==="+res.getInt(1));
}
}
//2.没有异常,提交事务
conn.commit();
flag = true;
} catch (Exception e) {
try {
//3.出现异常,回滚事务
conn.rollback();
flag = false;
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return flag;
}
}
控制层servlet:
package com.wanbangee.servlet;
import com.wanbangee.service.BuyBookService;
import com.wanbangee.service.imp.BuyBookServiceImp;
public class BuyBookServlet {
public static void main(String[] args) {
String loginname = "jjm";
int bookId = 1;
BuyBookService service = new BuyBookServiceImp();
boolean flag = service.buyBook(loginname, bookId);
if(flag == true) {
System.out.println("买书成功");
}else {
System.out.println("买书失败");
}
}
}
使用JDBC调用函数:(调用函数和查询是一样的操作)
#在girls数据库中,创建函数myf1
DELIMITER $
CREATE FUNCTION myf1(m INT) RETURNS INT
BEGIN
RETURN m*100;
END $
SELECT myf1(10);
package com.wanbangee.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
//调用函数,和查询是一样的操作
public class Demo01 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstate = null;
ResultSet res = null;
try {
conn = DBUtil.getConnection();
String sql ="SELECT myf1(10)";
pstate = conn.prepareStatement(sql);//没有参数不用设置问号值
res = pstate.executeQuery();
while(res.next()) {
System.out.println(res.getInt(1));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
res.close();
pstate.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用JDBC调用存储过程:
#在girls数据库中,创建存储过程myp4
DELIMITER $$
USE girls $$
DROP PROCEDURE IF EXISTS myp4 $$
CREATE DEFINER=root@localhost PROCEDURE myp4(IN user_name VARCHAR(20),IN pass_word VARCHAR(20),OUT res VARCHAR(10))
BEGIN
DECLARE result INT;
SELECT COUNT(*) INTO result FROM admin WHERE username = user_name
AND PASSWORD = pass_word;
SELECT IF(result>0,'success','fail') INTO res;
END$$
DELIMITER ;
CALL myp4('mpp','mpp123',@abc);
SELECT @abc;
package com.wanbangee.jdbc;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.JDBCType;
import java.sql.SQLException;
public class Demo02 {
public static void main(String[] args) {
Connection conn = null;
CallableStatement cstate = null;
try {
conn = DBUtil.getConnection();
String sql ="CALL myp4(?,?,?)";
cstate = conn.prepareCall(sql);//获取CallableStatement对象
cstate.setString(1, "mpp");
cstate.setString(2, "mpp123");
cstate.registerOutParameter(3, JDBCType.VARCHAR);//注册返回值的类型
cstate.execute();
String result = cstate.getString(3);//获取返回值
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
cstate.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
DAO是数据访问层的操作,掌握DAO的各个组成部分及组成部分的代码编写;
DAO的执行顺序:DAOFactory —》DAOProxy—》DAOImpl;
一个DAO实际上就是一个JavaBean的组件模型。
开发中,一个表或者一个视图就存在一个Dao
表图书信息表(图书ID,编号,名称,作者,单价,出版日期,出版社):Dao实现增删改查
账户表(账户ID,真实姓名,身份证号码,生日,性别,省份,城市,余额):Dao实现增删改查
图书库存表(图书ID,库存数量):Dao实现增删改查