JDBC—Dao模式的学习
- Dao模式是一种软件工程中使用的设计模式,用于将业务逻辑与持久性逻辑分离。它提供了一种访问数据库或其他数据源中的数据的方式,而不暴露数据源的底层细节。Dao模式通常与其他设计模式(如工厂模式)一起使用,以提供完整的数据访问和操作解决方案。
- Dao模式是用来:当我们完成一个业务需求需要使用资源时,如果直接调用资源层,就要直接对数据库进行操作,这时,业务层不光要完成具体业务的实现,还需要兼顾——编写SQL语句、连接数据库、和数据库进行交互、获得数据等等的操作。而且不利于一个程序的重用性。所以我们需要让业务层只来做业务实现,这时候和数据库的沟通交流就要交给DAO层来实现。
- Dao模式的结构,以本篇为例如图所示:
首先,我们需要建立一个数据库,以本篇为例我们建立一个my_shop_db数据库,在库中添加四张表格,商品分类表、商品表、用户表和订单表:tb_cateproy、tb_order、tb_prod、tb_user,方便之后获取Dao的实例化对象。建表语句如下,数据库信息的录入可以参考以下:
商品分类表:
--创建商品分类表:
CREATE TABLE `tb_category` (
`category_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '类目ID',
`shop_id` bigint NOT NULL COMMENT '店铺ID',
`parent_id` bigint unsigned NOT NULL COMMENT '父节点',
`category_name` varchar(50) NOT NULL DEFAULT '' COMMENT '产品类目名称',
`icon` varchar(255) DEFAULT NULL COMMENT '类目图标',
`pic` varchar(300) DEFAULT NULL COMMENT '类目的显示图片',
`seq` int NOT NULL COMMENT '排序',
`status` int NOT NULL DEFAULT '1' COMMENT '默认是1,表示正常状态,0为下线状态',
`rec_time` datetime NOT NULL COMMENT '记录时间',
`grade` int NOT NULL COMMENT '分类层级',
`update_time` datetime DEFAULT NULL COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--向表中录入数据
insert into tb_category values (85,1,0,'手机数码',null,null,1,1,'2019-04-21 17:28:33',0,'2019-04-30 18:00:25'),
(87,1,0,'美妆护肤',null,null,1,1,'2019-04-21 17:28:33',0,'2019-04-30 18:00:25'),
(88,1,0,'运动服饰',null,null,1,1,'2019-04-21 17:28:33',0,'2019-04-30 18:00:25'),
(93,1,85,'手机通信',null,null,1,1,'2019-04-21 17:28:33',0,'2019-04-30 18:00:25'),
(94,1,85,'智能设备',null,null,1,1,'2019-04-21 17:28:33',0,'2019-04-30 18:00:25'),
(95,1,0,'美味零食',null,null,1,1,'2019-04-21 17:28:33',0,'2019-04-30 18:00:25'),
(96,1,85,'珠宝钟表',null,null,1,1,'2019-04-21 17:28:33',0,'2019-04-30 18:00:25');
商品表:
--创建商品表
CREATE TABLE `tb_prod` (
`prod_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '产品ID',
`prod_name` varchar(300) NOT NULL DEFAULT '' COMMENT '商品名称',
`shop_id` bigint DEFAULT NULL COMMENT '店铺id',
`ori_price` decimal(15,2) DEFAULT '0.00' COMMENT '原价',
`price` decimal(15,2) DEFAULT NULL COMMENT '现价',
`brief` varchar(500) DEFAULT '' COMMENT '简要描述,卖点等',
`content` text COMMENT '详细描述',
`pic` varchar(255) DEFAULT NULL COMMENT '商品主图',
`imgs` varchar(1000) DEFAULT NULL COMMENT '商品图片,以,分割',
`status` int DEFAULT '0' COMMENT '默认是1,表示正常状态, -1表示删除, 0下架',
`category_id` bigint unsigned DEFAULT NULL COMMENT '商品分类',
`sold_num` int DEFAULT NULL COMMENT '销量',
`total_stocks` int DEFAULT '0' COMMENT '总库存',
`delivery_mode` json DEFAULT NULL COMMENT '配送方式json见TransportModeVO',
`delivery_template_id` bigint DEFAULT NULL COMMENT '运费模板id',
`create_time` datetime DEFAULT NULL COMMENT '录入时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`putaway_time` datetime DEFAULT NULL COMMENT '上架时间',
`version` int DEFAULT NULL COMMENT '版本 乐观锁'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--录入数据
insert into tb_prod() values (18,'Apple iphone XS Max 移动版',1,0.00,1.01,'6.5英寸大屏,双卡双待',null,null,null,1,93,null,null,null,null,'2019-04-21 17:28:33','2019-04-30 18:00:25','2019-04-30 18:00:25',null);
创建订单表和用户表:
--创建商品订单表
create table tb_order(
order_id int not null auto_increment primary key ,
order_number varchar(40) not null comment '订单号',
user_id int not null comment '用户编号',
total double not null comment '订单总价',
acutal_total int comment '实付价格',
status varchar(30) comment '支付状态',
pay_time datetime comment '支付时间',
is_payed varchar(20) comment '是否支付'
);
--用户表
create table tb_user(
user_id int primary key not null comment '用户编号',
user_name varchar(50) not null comment '用户登录名',
login_password varchar(40) not null comment '登录密码',
real_name varchar(30) default null comment '真名',
mail varchar(200) default null comment '邮箱',
mobile varchar(50) not null comment '电话',
status int not null comment '默认状态0或者1'
);
其次,在整个DAO中实际上都是以接口为操作标准的,即:客户端依靠DAO实现的接口进行操作,而服务端要将接口进行具体的实现。DAO由以下几个部分组成。
- DbHelper:专门负责数据库的打开与关闭,或者操作进行的类,在util包中;
- Entity实例对象:主要由属性、setter、getter方法组成,类中的属性与表中的字段相对应,每一个类的对象都表示表中的每一条记录;
- DAO:主要定义操作的接口,定义一系列数据库的原子性操作,例如:增加、修改、删除、查询等;Impl : DAO接口的真实实现类,完成具体的数据库操作,但是不负责数据库的打开和关闭;
- Service:对Dao的接口进行操作,实现定义的方法,这里是一个优化的方法。将Dao接口和其实例,要功能在Service层优化和实现;
- Test:定义简单的测试类进行相关测试。
最后以User为例,操作如下,写工具类DbHelper,建立数据库连接,封装增删改查的方法。
// DbHelper类
public class DbHelper {
private static String URL;
private static String USER;
private static String PWD;
static {//执行一次,执行一次
Properties properties = new Properties();
try {
InputStream inputStream = DbHelper.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(inputStream);
URL = properties.getProperty("URL");
USER = properties.getProperty("USER");
PWD = properties.getProperty("PWD");// 根据属性名字,获取属性值
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取连接
private static Connection getConn() throws SQLException {
return DriverManager.getConnection(URL,USER,PWD);
}
// 使用listMAp 进行数据操作,封装一个查询方法
public static List<Map<String,Object>> Query(String sql,Object... params){
Connection connection =null;
PreparedStatement statement = null;
ResultSet resultSet = null;
List<Map<String,Object>> list = new ArrayList<>();
try {
connection = getConn();
statement = connection.prepareStatement(sql);
setParams(statement,params);//调用方法绑定参数
resultSet = statement.executeQuery();
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
while (resultSet.next()) { //逐行读取,next():移动指针到 下一行,判断是否读到数据,一次读取一行
Map<String, Object> rowMap = new HashMap<>(); //代表一行
for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
// getColumnLabel():获取列的别名,如果没有就获取列名
rowMap.put(resultSetMetaData.getColumnLabel(i), resultSet.getObject(i) == null ? " " : resultSet.getObject(i));
}
list.add(rowMap);//填充list
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(connection, statement, resultSet);//调用关闭方法
}
return list;
}
// 关闭方法
private static void closeAll(Connection connection, PreparedStatement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}
// 更新操作
public static boolean update(String sql, Object... params) {
Connection connection = null;
PreparedStatement statement = null;
boolean result = false;
try {
connection = getConn();
statement = connection.prepareStatement(sql);//预编译
setParams(statement, params);//调用方法绑定参数
int n = statement.executeUpdate();
if (n > 0) {
result = true;
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
closeAll(connection, statement, null);
}
return result;
}
// 设置参数
private static void setParams(PreparedStatement statement,Object[] params) throws SQLException {
if (params != null) {
for (int i = 0; i < params.length; i++) {
statement.setObject(i + 1, params[i]);
}
}
}
}
//创建db.properties文件,存放连接的数据库以及账号密码,方便修改(更换库表等)
//文件内容如下
#用于文件加载
URL = jdbc:mysql://localhost:3306/my_shop_db?serverTimeZone=asia/shanghai&rewriteBatchedStatements=true
USER = root
PWD = MySQL
按照Dao模式进行如下操作,实现简单的用户登录。代码示例如下:
//entity实例类,user-对应tb_user表,选取关键字段写入
public class User {
private int userId;
private String userName;
private String loginPassword;
private String realName;
private int status;
public User() {
}
public User(int userId, String userName, String loginPassword, String realName, int status) {
this.userId = userId;
this.userName = userName;
this.loginPassword = loginPassword;
this.realName = realName;
this.status = status;
}
public User(String userName, String loginPassword) {
this.userName = userName;
this.loginPassword = loginPassword;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getLoginPassword() {
return loginPassword;
}
public void setLoginPassword(String loginPassword) {
this.loginPassword = loginPassword;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
@Override
public String
toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", loginPassword='" + loginPassword + '\'' +
", realName='" + realName + '\'' +
", status=" + status +
'}';
}
}
对数据表实例化后,我们要进行dao操作定义接口,Impl实现具体的数据库操作,可以书写一个商品订单的Dao模式,小伙伴们有兴趣可以进行尝试。
//创建UserDao接口
public interface UserDao {
List<Map<String,Object>> queryAll();
List<Map<String, Object>> login(User user); //登陆方法
}
//创建实现类UserDaoImpl类,实现接口 重写方法
public class UserDaoImpl implements UserDao {
@Override
public List<Map<String, Object>> queryAll() {
String sql = "select user_name, real_name,status from tb_user order by user_id desc";
return DbHelper.Query(sql);
}
@Override
public List<Map<String, Object>> login(User user) {
String sql = "select user_name,login_password from tb_user where user_name=? and login_password = ?";
return DbHelper.Query(sql,user.getUserName(),user.getLoginPassword());
}
}
//测试类 测试简单的登录方法
//执行此操作前,你的tb_user中需要有或者你录入一条user_name = laolang login_password = 123456这样的数据在数据库中存储。
public class Test1 {
private static UserDao userDao = new UserDaoImpl();
public static void main(String[] args) {
String userName = "laolang";
String loginpassword = "123456";
User user = new User(userName,loginpassword);
List<Map<String, Object>> list = userDao.login(user);
if (list.size() > 0) {
System.out.println("登录成功");
} else {
System.out.println("登录失败");
}
}
}
结果:
欢迎大家的学习,我们下期再见!