前言❤️❤️
hello hello💕,这里是洋不写bug~😄,欢迎大家点赞👍👍,关注😍😍,收藏🌹🌹
这篇博客是MySQL入门专栏的最后一篇博客🐵,会解析Java程序中的三层架构,这个在做Java项目时是必用的,MySQL入门专栏的知识基本上已经够应对基础项目了🐵
🎇个人主页:洋不写bug的博客
🎇所属专栏:数据库
🎇mysql8.0和navicate的安装:mysql安装教程
🎇铁汁们对于MySQL数据库的各种常用核心语法,都可以在上面的数据库专栏学习,专栏正在持续更新中🐵🐵,有问题可以写在评论区或者私信我哦~
1,表的准备
在数据库中创建一个学生表,一个班级表,插入数据,sql语句如下所示:
CREATE TABLE IF NOT EXISTS class (
id BIGINT PRIMARY KEY COMMENT '班级ID',
name VARCHAR(20) COMMENT '班级名称'
) COMMENT '班级信息表';
INSERT INTO class (id, name)
VALUES
(1, 'Java班'),
(2, 'C++班'),
(3, 'python班');
CREATE TABLE IF NOT EXISTS student (
id BIGINT PRIMARY KEY COMMENT '学生ID',
name VARCHAR(20) COMMENT '学生姓名',
sno VARCHAR(10) COMMENT '学号',
age INT COMMENT '年龄',
gender TINYINT(1) COMMENT '性别(1表示男)',
enroll_date DATE COMMENT '入学日期',
class_id BIGINT COMMENT '所属班级ID'
) COMMENT '学生信息表';
INSERT INTO student (id, name, sno, age, gender, enroll_date, class_id)
VALUES
(1, '唐三藏', '100001', 20, 1, '1986-09-01', 1),
(2, '孙悟空', '100002', 18, 1, '1986-09-01', 1),
(3, '猪悟能', '100003', 18, 1, '1986-09-01', 1),
(4, '沙悟净', '100004', 18, 1, '1986-09-01', 1),
(5, '宋江', '200001', 18, 1, '1986-09-01', 1),
(6, '武松', '200002', 18, 1, '2000-09-01', 2),
(7, '李逵', '200003', 18, 1, '2000-09-01`在这里插入代码片`', 2),
(8, '不想毕业', '200003', 18, 1, '2000-09-01', 2);
为了简化JDBC操作,建议写一个DBUtil类,里面把连接代码什么的都写好,再搞两个close方法(方法重载),一个是有resultset,一个没有,后面关闭资源的时候方便调用,sql如下所示:
package utils;
import com.mysql.cj.jdbc.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBUtil {
//定义数据源对象
private static DataSource dataSource;
//定义URL
private static String URL = "jdbc:mysql://127.0.0.1:3308/first_database?characterEncoding=utf8" +
"&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false";
//定义用户名
private static String USER = "root";
//定义密码
private static String PASSWORD = "123456";
//在类加载的时候初始化dataSource对象
static {
MysqlDataSource mysqlDataSource = new MysqlDataSource();
mysqlDataSource.setURL(URL);
mysqlDataSource.setUser(USER);
mysqlDataSource.setPassword(PASSWORD);
dataSource = mysqlDataSource;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void close(ResultSet resultSet,Statement statement,Connection connection){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
public static void close(Statement statement,Connection connection){
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
//构造方法私有化,防止其他地方通过new来创建这个类的对象
private DBUtil(){}
}
2,三层架构
这里创建4个包,utils包中放前面的DBUtil类,方便后续连接数据库,如下所示:
这个Dao包,Entity包,以及Service包,就是我们常说的数据库中的三层架构
- Entity层:实体层,例如这个项目中数据库中有user表,class表
那就把它们写成user类和class类放到entity层中,user表和class表中的列就对应着类中创建的私有属性 - Dao层:数据访问层,Dao层中只负责数据的访问,向数据库传入增删改查的SQL指令,Dao层不负责任何逻辑判断(例如不判断插入数据是否合理,逻辑的判断是在service层实现的)
- Service层:业务逻辑层,负责处理业务逻辑,例如进行逻辑判断(例如校验班名是否重复,进行事务控制,操作多张表等)
例如想要插入数据,就是这样一个流程:首先在main方法中读取用户输入,接着main方法调用Service层,在Service层中对数据进行校验,或者执行其他逻辑;Service层再调用Dao层,访问数据库中的数据;
Dao层插入数据后,将是否插入成功返回给service层,service层再返回给main层使用三层架构好处如下:
- 代码耦合度低,修改代码和扩展时都比较方便,例如要把数据库从 MySQL 换成 Oracl,只需要修改 Dao 层的 SQL 语法即可
- 代码结构清晰,方便在测试时快速定位bug
- 可以提高代码的复用率,写一次「查询用户」的方法,所有业务场景(登录、修改信息、统计)都能直接调用,不用在每个功能里复制粘贴相同的代码,大幅减少冗余
现在在Entity层中创建一个学生类,一个班级类
在Dao层里面创建两个类,一个负责班级信息对数据库的访问,一个负责学生信息对数据库的访问
在Service层中创建两个类,一个里面写有关学生信息的读取和修改的逻辑,一个里面写有关班级信息的读取和修改的逻辑

在实体层中写两个类,一个Classes类,代表班级(Class没办法做类名)
一个Student类,代表学生,两个类中的属性就是表中的列名,再写下get和set方法代码如下:package Entity; public class Classes { private long id; private String name; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }package Entity; import java.util.Date; public class Student { private long id; private String name; private String sno; private Integer age; private byte gender; private Date enroll_date; private long class_id; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSno() { return sno; } public void setSno(String sno) { this.sno = sno; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public byte getGender() { return gender; } public void setGender(byte gender) { this.gender = gender; } public Date getEnroll_date() { return enroll_date; } public void setEnroll_date(Date enroll_date) { this.enroll_date = enroll_date; } public long getClass_id() { return class_id; } public void setClass_id(long class_id) { this.class_id = class_id; } }3,实现班级插入
下面就用三层架构来实现班级插入
首先在ClassDao类中写一个insert方法,这个insert方法接收的是一个Classes类的对象,返回一个int值,如果int等于1,表示插入成功了,返回0就是插入失败了,代码如下:
package Dao; import Entity.Classes; import utils.DBUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class ClassDao { public int insert(Classes record){ Connection connection = null; PreparedStatement preparedStatement = null; try { connection = DBUtil.getConnection(); String sql = "insert into class(name) values (?)"; preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1,record.getName()); int i = preparedStatement.executeUpdate(); return i; } catch (SQLException e) { throw new RuntimeException(e); }finally { DBUtil.close(preparedStatement,connection); } } }接着就是ClassService,创建一个私有化的ClassDao对象,方便在Service层来调用Dao层,写一个addClass方法,返回值是int
这里就可以在Service层的addClass方法中加一些判断逻辑,例如判断输入的班级名之前是否已经重复了(这就需要在ClassDao再写一个findByName方法来访问数据库),或者输入必须按照一定的格式(这里博主就不加了)
package Service; import Dao.ClassDao; import Entity.Classes; public class ClassService { private ClassDao classDao = new ClassDao(); public int addClass(Classes classes){ return classDao.insert(classes); } }接着在demo包里面写一个Test类来读取下用户输入的班级名,创建一个班级对象,再创建个ClassService对象,方法调用一下即可
package demo; import Entity.Classes; import Service.ClassService; import java.util.Scanner; public class Test { public static void main(String[] args) { System.out.println("请输入要添加的班级名:"); Scanner scanner = new Scanner(System.in); String name = scanner.next(); Classes classes = new Classes(); classes.setName(name); ClassService service = new ClassService(); int row = service.addClass(classes); if(row == 1){ System.out.println("插入成功"); }else{ System.out.println("插入失败"); } } }
4,lombok的使用
在JDBC第一篇博客中,提到从Maven仓库中下载JDBC驱动,Maven仓库中有很多实用的工具库,框架,例如lombok,就是一个非常实用的工具库,接下来介绍下它的使用
打开检索平台(链接放在下面了),搜索lombok,打开第一个,再选择1.18版本,复制下面文本框中的内容
Maven依赖检索平台


把内容粘贴到项目的pom.xml中的dependencies标签中(这部分不明白的铁汁可以看下博主JDBC的第一篇博客,里面详细介绍了驱动的引入),点击一下右上角的这个按钮,Maven 就会从中央仓库下载这个工作库,并将其引入到项目中

使用IDEA的铁汁要在设置的Plugins中下载一个Lombok插件,这样才能正常使用

这个Lombok的作用就是通过通过注解自动生成getter/setter、构造器、toString 等代码,进而减少代码的冗余
就比如在Classes类中,之前要我们手动生成get,set,构造方法,看起来代码很长,引入了lombok工具库后,只需要加个@Data,就相当于把这些方法都添加进去了,非常方便(这些代码会在编译时自动生成)
可以点击Structure看一下,虽然这里我们什么都没写,但是Classes类中的这些方法已经自动生成了,是不是非常方便🐵

5,根据班级名查找同学信息
如果想要达到下图这样根据班名查找同学信息的效果,我们分析下用三层架构怎么搞

其实查找跟插入的套路大概是一样的,插入就是在main方法中创建个对象,调用Sercice层,Sercice层再把对象传给Dao层,Dao层提取对象中的信息后插入到数据库中(可参考班级的插入的步骤)
查找只是反了一下,main方法调用Sercice层的查找方法,传入参数,Sercice层再调用Dao层,把参数传给Dao层,dao层查询到数据后以对象的形式传给Sercice层,Sercice层再把对象传给main方法,这样就查询成功了
这里就需要解决两个问题- 查询时结果中要求有班级名称,但是如果返回学生对象,学生对象中只有班级编号,是没有班级名称的
- 我们查询到的是多行数据,只返回单个对象肯定是不行的
对于第一个问题,就是在Student类中创建个复合变量,把Classes类放进去当作一个属性,这样后续main方法得到Student对象后,就可以提取出班级名
对于第二个问题,因为查询到的是多行数据,可以把每一行创建成一个student对象,搞一个链表,把这些student对象储存到链表中,最后再把链表返回给main方法
Student类代码如下所示(这里我们测试下lombok帮我们生成的toString方法,就不再重写toString方法了)
package Entity; import lombok.Data; import java.util.Date; @Data public class Student { private long id; private String name; private String sno; private Integer age; private byte gender; private Date enroll_date; private long class_id; private Classes classes; }在StudentDao中,写一个selectByClassName,核心就是用while循环读取resultSet,然后创建student对象,把读取到的数据set给对象,再创建链表,最后返回链表
package Dao; import Entity.Classes; import Entity.Student; import utils.DBUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; import java.util.List; public class StudentDao { public List<Student> selectByClassName(String className){ Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { connection = DBUtil.getConnection(); String sql = "select s.id,s.name,s.sno,s.age,s.gender,s.enroll_date,s.class_id,c.name class_name" +" from student s,class c where s.class_id = c.id and c.name = ?"; preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1,className); resultSet = preparedStatement.executeQuery(); List<Student> list = null; while (resultSet.next()){ long id = resultSet.getLong("id"); String name = resultSet.getString("name"); String sno = resultSet.getString("sno"); int age = resultSet.getInt("age"); byte gender = resultSet.getByte("gender"); Date enroll_date = resultSet.getDate("enroll_date"); long class_id = resultSet.getLong("class_id"); String class_name = resultSet.getString("class_name"); Student student = new Student(); student.setId(id); student.setName(name); student.setSno(sno); student.setAge(age); student.setGender(gender); student.setEnroll_date(enroll_date); student.setClass_id(class_id); //创建班级对象 Classes classes = new Classes(); classes.setName(className); classes.setId(class_id); //学生和班级建立联系 student.setClasses(classes); if(list == null){ list = new ArrayList<>(); } list.add(student); } return list; } catch (SQLException e) { throw new RuntimeException(e); } } }接着再写下Studentervice类,创建StudentDao对象,接着进行输入校验(根据实际要求来写),如果有输入异常的情况,就抛出异常
输入校验通过后,再调用StudentDao对象的selectByClassName,把返回的list再返回给main方法package Service; import Dao.StudentDao; import Entity.Student; import java.util.List; public class StudentService { private StudentDao studentDao = new StudentDao(); public List<Student> getStudentsByClassName(String className) { // 1. 空值验证 if (className == null) { throw new IllegalArgumentException("班级名不能为null"); } // 2. 空字符串验证 String trimmedClassName = className.trim(); if (trimmedClassName.isEmpty()) { throw new IllegalArgumentException("班级名不能为空字符串"); } // 3. 长度验证 if (trimmedClassName.length() > 50) { throw new IllegalArgumentException("班级名长度不能超过50个字符"); } return studentDao.selectByClassName(trimmedClassName); } }接着在main方法中,就通过调用service层,得到返回的list结果,再遍历list即可,代码如下所示:
package demo; import Service.StudentService; import Entity.Student; import java.util.List; import java.util.Scanner; public class Test2 { public static void main(String[] args) { System.out.println("请输入班级名->"); Scanner scanner = new Scanner(System.in); String className = scanner.next(); StudentService studentService = new StudentService(); List<Student> list = studentService.getStudentsByClassName(className); if(list == null || list.isEmpty()){ System.out.println("没有查询到结果"); return; } for(Student stu : list){ System.out.println(stu.toString()); } } }
结语💕💕
- 这篇博客是数据库基础专栏的最后一篇博客 使用数据库三层架构是写程序时的基础要求,这部分在写的时候因为有两次调用,也是很容易出bug的
- 刚学习的铁汁,如果有报错的话,不要怕复杂,可以结合报错信息来排查bug,从main方法调用处,看到Service层,再看到Dao层,分析出bug
数据库专栏的内容到这里就更新完了~完结撒花~🥳🎉🎉



:三层架构基础解析&spm=1001.2101.3001.5002&articleId=158573008&d=1&t=3&u=f17f4bc85f9440dda4dfbce0258550f9)
1220

被折叠的 条评论
为什么被折叠?



