三层架构
三层架构是一种软件设计结构 ,是一种组织代码的手段和方式,在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。推荐的分层式结构一般分为三层,从上至下分别为:表示层、业务逻辑层(又或称为领域层)、持久层。
区分层次的目的是为了高内聚低耦合的思想。
通常意义上就是将整个业务应用划分为:
表示层:数据的显示或者录入
业务逻辑层:业务的具体操作流程
持久层:操作数据库,针对数据库表CRUD(增删改查)
三层架构的实现
dao:持久层
entity:实体类
service:业务层
utils:工具类
view:表示层
要以三层架构来实现一个业务,我们要从几个方面入手,按先后顺序来区分:
1、分析需求
2、拆解业务逻辑
3、确定调用关系
4、定义接口
以登录业务为例:
业务需求:用户输入用户名和密码,检查,正确就登录,错误给出提示信息
表示层:
采集用户输入,显示错误信息
输入:String用户名,String密码,
输出:user对象(包含username和password属性)
显示错误信息(输入:login对象,输出:String login.errorInfo值)
业务逻辑层:检查用户的用户名和密码,判断是否允许登录
校验用户密码,判断是否允许登录
输入:login对象,输出:boolean
持久层:查询用户名和用户密码
查询用户密码
输入:user对象,
输出:String username, String password)
如果没查到用户,返回null,否则返回数据库中保存的密码。
判断是否允许登录
输入:login对象,输出:boolean
实现实体类
实体类
属性一般用private修饰,getter/setter方法,重写tostring等可使用Lombok及相应架包来大大减少代码量,仅需@Data就可以完成完成。
对实体类提供无参构造方法,根据业务需要提供相应的有参构造方法,可以利用lombok的@AllArgsConstructoh和@NoArgsConstructor来提供分别提供有参构造方法和无参构造方法;
接口类
接口类的定义要根据业务需求,在逻辑层的调用需求,都需要在接口层定义相应的接口。
只要是实现了此接口的实现类,都可以在逻辑层被正常调用,这样就实现了底层实现类更新扩展替换,而不影响到逻辑层代码的目的。
接口的实现类
接口的实现类具体实现接口逻辑,根据底层存储的不同,可以有多种不同的实现类,每个实现类对应一种底层存储。
数据库操作工具类
将通用操作(数据库打开连接,关闭连接)封装到工具类中,避免数据库连接和关闭代码的重复使用,方便修改。
public class DButils{
private String driver = "com.mysql.cj.jdbc.Driver";// 数据库驱动字符串,MySQL版本为8,mysql5的去掉cj即可
private String url = "jdbc:mysql://localhost:3306/jdbc";// 连接URL字符串
private String user = "root"; // 数据库用户名
private String password = "root"; // 用户密码
Connection conn = null; // 数据连接对象
/**
* 获取数据库连接对象
*/
public Connection getConnection() {
if(conn==null) {
// 获取连接并捕获异常
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace(); // 异常处理
}
}
return conn; // 返回连接对象
}
/**
* 关闭数据库连接
* @param conn 数据库连接
* @param stmt Statement对象
* @param rs 结果集
*/
public void closeAll(Connection conn, Statement stmt, ResultSet rs) {
// 若结果集对象不为空,则关闭
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 若Statement对象不为空,则关闭
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 若数据库连接对象不为空,则关闭
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
实现通用的增删改操作(采用预处理SQL模式 )。
/**
* 增、删、改的操作
* @param sql 预编译的 SQL 语句
* @param param 参数的字符串数组
* @return 影响的行数
*/
public int exceuteUpdate (String preparedSql, Object[] param) {
PreparedStatement pstmt = null;
int num = 0;
conn = getConnection();
try {
pstmt = conn.prepareStatement(preparedSql);
if (param != null) {
for (int i = 0; i < param.length; i++) {
//为预编译sql设置参数
pstmt.setObject(i + 1, param[i]);
}
}
num = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally{
closeAll(conn,pstmt,null);
}
return num;
}
其他部分,根据业务具体需求去实现
Lombok
可以帮我们省略setter/getter,tostring这些代码
使用步骤
1、安装Lombok插件
File—Settings—Plugins,搜索Lombok
2、导包
lombok-1.18.24.jar
3、使用
package com.blb.demo3.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* EmployeeId Varchar(20) 员工帐号
* Password Varchar(20) 登录密码
* EmployeeName Varchar(50) 姓名
* Age Int 年龄
* Salary Decimal(12,3) 工资
*/
@Data //提供set、get,toString方法
@AllArgsConstructor //提供有参构造方法
@NoArgsConstructor //提供无参构造方法
public class ErpEmployee {
private String EmployeeId;
private String Password;
private String EmployeeName;
private int Age;
private double Salary;
}
Java连接池
当我们创建了一个Connection对象,它在内部都执行了什么:
1.“DriverManager”检查并注册驱动程序;
2.“com.mysql.jdbc.Driver”就是我们注册了的驱动程序,它会在驱动程序类中调用“connect(url…)”方法。
3.com.mysql.jdbc.Driver的connect方法根据我们请求的“connUrl”,创建一个“Socket连接”,连接到IP为“your.database.domain”,默认端口3306的数据库。
4.创建的Socket连接将被用来查询我们指定的数据库,并最终让程序返回得到一个结果。
简单的获取一个连接,系统却要在背后做很多消耗资源的事情,大多时候,创建连接的时间比执行sql语句的时间还要长。
为解决上述问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。
原理:
先打开一定数量的数据库连接,当使用的时候分配给调用者,调用完毕后返回给连接池,注意返回给连接池后这些连接并不会关闭,而是准备给下一个调用者进行分配。由此可以看出连接池节省了大量的数据库连接打开和关闭的动作,对系统性能会有较大地提升。
Spring—JDBC
一个由Spring团队开发的JDBC工具类,作用和DBUtils相同,目前作为代替DBUtils的工具
使用步骤:
1.导包
spring-beans-5.3.22.jar
spring-core-5.3.22.jar
spring-jcl-5.3.22.jar
spring-jdbc-5.3.22.jar
spring-tx-5.3.22.jar
2.创建JdbcTemplate工具
package com.blb.utils;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.io.IOException;
import java.util.Properties;
public class JdbcTemplateUtils {
private static HikariDataSource hikariDataSource;
private static JdbcTemplate jdbcTemplate;
private static DataSourceTransactionManager transactionManager;
private static DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
static{
try {
Properties properties = new Properties();
properties.load(JdbcTemplateUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"));
HikariConfig hikariConfig = new HikariConfig(properties);
hikariDataSource = new HikariDataSource(hikariConfig);
transactionManager = new DataSourceTransactionManager(hikariDataSource);
} catch (IOException e) {
System.out.println("配置文件找不到,使用默认配置");
}
}
/**
* 获取JdbcTemplate
* @return
*/
public static JdbcTemplate getJdbcTemplate(){
if (jdbcTemplate==null) {
jdbcTemplate = new JdbcTemplate(hikariDataSource);
}
return jdbcTemplate;
}
/**
* 开启事务
* @return
*/
public static TransactionStatus beginTransaction(){
return transactionManager.getTransaction(transactionDefinition);
}
/**
* 开启事务
* @param transactionDefinition
* @return
*/
public static TransactionStatus beginTransaction(TransactionDefinition transactionDefinition){
return transactionManager.getTransaction(transactionDefinition);
}
/**
* 提交事务
* @param transactionStatus
*/
public static void commit(TransactionStatus transactionStatus){
transactionManager.commit(transactionStatus);
}
/**
* 回滚事务
* @param transactionStatus
*/
public static void rollback(TransactionStatus transactionStatus){
transactionManager.rollback(transactionStatus);
}
}
3.使用JdbcTemplate完成CRUD操作
package com.blb.demo3.dao.impl;
import com.blb.demo3.dao.IEmployeeDao;
import com.blb.demo3.entity.ErpEmployee;
import com.blb.demo3.utils.JdbcTemplateUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class EmployeeDaoImpl implements IEmployeeDao {
JdbcTemplate jdbcTemplate = JdbcTemplateUtils.getjdbcTemplate();
/**
* 新增
* @param ev
* @throws Exception
*/
@Override
public void create(ErpEmployee ev) throws Exception {
jdbcTemplate.update("insert into erp_employee(EmployeeId, Password, EmployeeName, Age, Salary) values (?,?,?,?,?)",
ev.getEmployeeId(), ev.getPassword(), ev.getEmployeeName(), ev.getAge(), ev.getSalary());
}
/**
* 修改
* @param ev
* @throws Exception
*/
@Override
public void update(ErpEmployee ev) throws Exception {
jdbcTemplate.update("update erp_employee set Password = ?, EmployeeName = ?, Age = ?, Salary = ? where EmployeeId = ?",
ev.getPassword(), ev.getEmployeeName(), ev.getAge(), ev.getSalary(), ev.getEmployeeId());
}
/**
* 删除
* @param ev
* @throws Exception
*/
@Override
public void delete(ErpEmployee ev) throws Exception {
jdbcTemplate.update("delete from erp_employee where EmployeeId = ?");
}
/**
* 查询
* @return
* @throws Exception
*/
@Override
public List<ErpEmployee> getAll() throws Exception {
return jdbcTemplate.query("select * from erp_employee",
new BeanPropertyRowMapper<ErpEmployee>(ErpEmployee.class));
}
}