当前位置: 首页 > news >正文

JAVASE-医疗管理系统项目总结

文章目录

    • 项目功能架构
    • 运行截图
    • 数据库设计
    • 设计模式应用
      • 单列设计模式JDBC模板
      • 模板设计模式
      • 策略模式
      • 工厂设计模式
      • 事务控制+代理模式
      • 注解开发优化工厂模式
    • 页面跳转
    • ThreadLocal
    • 分页查询实现
    • 统计模块
    • 聊天

项目功能架构

传统的MVC架构,JavaFX桌面端项目,前端用xml来编写,用工具拖拉拽生成前端。

分为医生模块、药品模块和病人模块,每一个模块都有controller、service、dao、pojo层。

框架采用JDBC,手写了映射、控制反转和依赖注入。

运行截图

image-20240718185946673

​ 根据医生的role来显示不同的页面

​ 登录

image-20240718190000554

​ 主页

实现了组合查询 和分页查询

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以添加和修改医生

image-20240718190813312

image-20240718190826041

image-20240718190046518

挂号

image-20240718190058557

image-20240718190137643

缴费表 花费+挂号费用25,后面还可以根据不同医生的不同级别还指定挂号费用。

image-20240718190243621

添加药品种类和供应商

image-20240718190339365

采购药品

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据统计

image-20240718190430600

image-20240718190446460

系统设置 可以修改密码和切换用户

实现聊天功能(带完善)

image-20240718190521876

数据库设计

一共十一张表结构。

image-20240718172006128

设计模式应用

单列设计模式JDBC模板

Singleton 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

场景:单例模式只应在有真正的“单一实例”的需求时才可使用。

单例获得数据库连接关闭数据库连接工具类

采用的是 饿汉式单列模式,在类加载时创建实例,不管后期是否使用都会创建

private static JDBCUtil instance = new JDBCUtil();  
//初始的一个数据库连接,和数据库值建立一个连接,保证了后面每次不用再关闭流

模板设计模式

简化JDBC开发,原来每个方法都要编写好多的

MyJdbcTemplate类中的方法(如queryObject、insert、queryAll和executeUpd)实现了一种通用的操作步骤,而具体的操作(如如何将结果集映射到对象)由子类或调用方实现。通过这种方式,代码复用了公共的数据库操作步骤,同时允许调用方定制具体的行为。package com.NJITZX.common.dao;import com.NJITZX.common.anno.Component;
import com.NJITZX.common.exceptions.DataException;
import com.NJITZX.common.util.JDBCUtil;
import org.apache.commons.lang3.StringUtils;import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;@Component
public final class MyJdbcTemplate {//查询一条记录public <T> T queryObject(final String SQL, RowMapper<T> rowMapper, Object... parms) throws DataException {Connection con = JDBCUtil.getCon(); //获得连接try {PreparedStatement pst = con.prepareStatement(SQL);//设置参数setParmas(pst, parms); //设置参数ResultSet rst = pst.executeQuery();if (rst.next()) {T entity = rowMapper.mapper(rst);  //mapper是将rst中值 映射到对象上面来 并返回来;
//                System.out.println(entity);return entity;}} catch (SQLException e) {e.printStackTrace();throw new DataException("查询记录失败");}return null;}//添加用户 返回主键//可变参数public int insert(final String SQL, Object... parms) throws DataException {Connection con = JDBCUtil.getCon();try {PreparedStatement pst = con.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS); //设置返回的主键值setParmas(pst, parms);int row = pst.executeUpdate();if (row < 1) {throw new DataException("新增记录,返回主键值出错");}ResultSet rst = pst.getGeneratedKeys(); //得到生成keysif (rst.next()) {return rst.getInt(1); //返回第一列}} catch (SQLException e) {e.printStackTrace();throw new DataException(e.getMessage());}return 0;}//查询多条语句public <T> List<T> queryAll(final String SQL, RowMapper<T> rowMapper, Object... parms) throws DataException {List<T> list = new ArrayList<>();Connection con = JDBCUtil.getCon();try {PreparedStatement pst = con.prepareStatement(SQL);//设置参数setParmas(pst, parms);ResultSet rst = pst.executeQuery();while (rst.next()) {T entity = rowMapper.mapper(rst);list.add(entity);}} catch (SQLException e) {e.printStackTrace();throw new DataException(e.getMessage());}return list;}//增删改public boolean executeUpd(final String SQL, Object... parms) throws DataException {Connection con = JDBCUtil.getCon(); //得到当前的连接try {System.out.println(SQL);PreparedStatement pst = con.prepareStatement(SQL);setParmas(pst, parms);return pst.executeUpdate() > 0;} catch (SQLException e) {e.printStackTrace();throw new DataException(e.getMessage());}}private void setParmas(PreparedStatement pre, Object[] parms) throws SQLException {if (parms != null && parms.length > 0) {for (int i = 0; i < parms.length; i++) {pre.setObject(i + 1, parms[i]);}}}
}

策略模式

RowMapper接口及其实现类DrugMapper体现了策略模式。RowMapper接口定义了一个映射结果集的方法,而DrugMapper提供了具体的映射实现。在执行数据库查询时,可以通过传入不同的RowMapper实现类来改变结果集的映射方式。ORM(Object-Relational Mapping,面向对象关系映射)是一种通过使用面向对象编程语言来处理数据库的技术。ORM提供了一种从面向对象编程语言到关系型数据库之间的转换机制,使得开发者可以使用对象来直接操作数据库中的数据,而不需要编写复杂的SQL查询。ORM使得开发者可以使用面向对象的方式操作数据库,无需编写大量的SQL语句,简化了代码编写。
//定义了接口
public interface RowMapper <T>{T mapper(ResultSet res) throws DataException;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//实现了 数据库里面的字段 到对象之间的映射关系
public class DrugMapper implements RowMapper<Drug> {@Overridepublic Drug mapper(ResultSet res) throws DataException {Drug drug=new Drug();try {drug.setDrugId(res.getInt(1));drug.setDrugName(res.getString(2));drug.setDrugType(res.getInt(3));drug.setSupport(res.getInt(4));drug.setProdectDate(res.getString(5));drug.setEffectDate(res.getInt(6));drug.setAmount(res.getInt(7));drug.setPrice(res.getDouble(8));drug.setDescp(res.getString(9));drug.setSellPrice(res.getDouble(10));} catch (SQLException e) {e.printStackTrace();}return  drug;}
}

消息弹出框采用的也是策略模式,写的时候采用匿名内部类的形式

public interface SystemConfirm {void execute();
}
public class SystemInfo {//提供系统的确认框public static void getConfirm(String title, String msg, SystemConfirm info) {Alert alert = new Alert(Alert.AlertType.CONFIRMATION);alert.setTitle(title);alert.setContentText(msg);Optional<ButtonType> buttonType = alert.showAndWait();if (buttonType.get() == ButtonType.OK)info.execute();}
}

调用

    public void deleteDoctor() throws ServiceException {//匿名内部内SystemInfo.getConfirm("删除医生", "确定删除医生吗?", new SystemConfirm() {@Overridepublic void execute() {int curID = getSelectID();if (curID != 0) {//不为0 开始进行操作;try {boolean b = doctorService.deleteById(curID);if (b == true) {AlertUtil.alertInfo("恭喜删除成功");loadPage();//重新加载所有的医生;}} catch (ServiceException e) {e.printStackTrace();}}}});在这个代码中,SystemConfirm接口和它的匿名实现类(在deleteDoctor方法中)体现了策略模式。SystemConfirm接口定义了execute方法,而具体的删除医生操作则是在匿名实现类中实现的。

工厂设计模式

Factory 定义一个工厂类,对实现同一个接口的一组类进行实例化对象操作

采用了IOC控制反转的思想,主动new一个对象,变为把对象交给工厂去创建,我们直接从工厂中去拿这个对象即可。 这里还用到了反射的思想

public abstract class BeanFactory {private static Map<String, Object> beanMap = new HashMap<>();   //把对象放入到hashmao中去;/*实列化项目中所有的对象*/static {//读取bean.properties里面的所有属性到is流里面。  将所有的都初始化到map中去try {InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));  //字节流转换成字符流String data = null;while ((data = br.readLine()) != null) {String[] spilt = data.split("="); //对data进行分割String key=spilt[0]; //key是类名String cls=spilt[1];  //cls 引用值Class<?> c=Class.forName(cls);Object obj= null;try {try {obj = c.newInstance(); //通过反射得到对象} catch (IllegalAccessException e) {e.printStackTrace();}} catch (InstantiationException e) {e.printStackTrace();}beanMap.put(key,obj);}} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}//得到一个实列   getInstance();public  static Object getInstance(String   key){   //得到对应的keyif (key.contains("Service")){//进行事务的处理return TxProxy.getProxy(beanMap.get(key));}return  beanMap.get(key);}
}

bean.properties文件

image-20240718095740813

//实例化一个对象 
//这样就实现了接口与实现类之间的解耦,通过配置文件灵活地指定实现类。
public JobDao jobDao = (JobDao) BeanFactory.getInstance("JobDao");

事务控制+代理模式

事务的四大特性是ACID,原子性、一致性、隔离性。

多表之间的修改就涉及到事务,我们要确保两次修改要么同时执行成功,要么同时失败。所以在service中需要开启事务。

JDBC setAutoCommit() 关闭启动提交 commit() 提交事务 rollback() 回滚事务

image-20240718094230368

每一个Service都需要进行事务添加,这样明显是很复杂的,所以我们使用代理模式给每一个Service都添加开启事务。

代理模式可以认为是中间商的意思,通过这个中间商给你的service加了一点东西进去。

public interface TXManager {public  void  commitTX() throws DataException;//    public  voidpublic  void rooBackTx() throws  DataException;public  void  beginTX() throws  DataException;
}

import com.NJITZX.common.exceptions.DataException;
import com.NJITZX.common.tx.TXManager;
import com.NJITZX.common.util.JDBCUtil;import java.sql.SQLException;public class TXManagerImpl implements TXManager {@Overridepublic void commitTX() throws DataException {try {JDBCUtil.getCon().commit();} catch (SQLException e) {e.printStackTrace();throw new DataException("提交事务失败");}}@Overridepublic void rooBackTx() throws DataException {try {JDBCUtil.getCon().rollback();} catch (SQLException e) {e.printStackTrace();throw  new DataException("回顾事务失败");}}@Overridepublic void beginTX() throws DataException {try {JDBCUtil.getCon().setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();throw new  DataException("开启事务失败");}}
}

采用jdk的动态代理

所谓动态是指在程序运行前不存在代理类的字节码文件,代理类和委托类的关系是在程序运行时确

jdk技术:只适用于实现了接口的类,使用 java.lang.reflect.Proxy

import com.NJITZX.common.exceptions.DataException;
import com.NJITZX.common.tx.TXManager;
import com.NJITZX.common.tx.impl.TXManagerImpl;
import com.NJITZX.common.util.JDBCUtil;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.SQLException;public class TxProxy {private static TXManager txManager = new TXManagerImpl();//getProxy 方法是一个静态泛型方法,用于生成目标对象的代理。targetOb 是要代理的目标对象。public static <T> T getProxy(final Object targetOb) {//JVM类加载器ClassLoader loader = targetOb.getClass().getClassLoader();// 目标对象实现的接口数组Class<?>[] interfaces = targetOb.getClass().getInterfaces();//增强处理类/*** 代理对象* 被代理目标方法* 方法泪飙创建一个 InvocationHandler,它定义了代理对象的行为。
invoke 方法在代理对象调用任何方法时被调用。*/InvocationHandler h = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws DataException {try {txManager.beginTX();  //开启事务Object returnval = method.invoke(targetOb, args);txManager.commitTX(); //提交事务return returnval;} catch (Exception e) {e.printStackTrace();try {txManager.rooBackTx();  //出现异常回滚事务throw  new DataException("代理失败");} catch (DataException ex) {ex.printStackTrace();throw new DataException("代理失败");}}}};return (T) Proxy.newProxyInstance(loader, interfaces, h);/**使用 Proxy.newProxyInstance 方法创建代理对象。该方法需要类加载器、接口数组和处理器(InvocationHandler)。
返回类型转换后的代理对象。/}
}

注解开发优化工厂模式

最初的是new一个,后面是工厂模式,再进阶到抽象工厂。

抽象工厂模式还是有点不好,需要自己去写bean.properties文件,如果bean文件出错并且每次都需要再次添加。

IOC控制反转,将对象交给容器去创建。

//依赖注入
@Retention(RetentionPolicy.RUNTIME) //程序运行的时候执行
@Target(ElementType.FIELD)  //作用在属性上面
public @interface Autowired {
}

重写了Beanfactory

import com.NJITZX.common.anno.*;
import com.NJITZX.common.proxy.TxProxy;
import org.reflections.Reflections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;public abstract class BeanFactory {private static Map<Class, Object> beanMap = new HashMap<>();   //把对象放入到hashmao中去;//IOC容器static {//扫描包Reflections reflections = new Reflections("com.NJITZX");Set<Class<?>> comSet = reflections.getTypesAnnotatedWith(Component.class);Set<Class<?>> respSet = reflections.getTypesAnnotatedWith(Respository.class);Set<Class<?>> serviceSet = reflections.getTypesAnnotatedWith(Service.class);//将扫描的信息存放集合Set<Class<?>> beanClassSet = new LinkedHashSet<>();beanClassSet.addAll(comSet);beanClassSet.addAll(respSet);beanClassSet.addAll(serviceSet);//反射创建对象beanClassSet.forEach(beanClass -> {try {//通过反射创建对象Object obj = beanClass.newInstance();//获取类实现的接口Class<?>[] interfaces = beanClass.getInterfaces();//如果没有接口Class key = beanClass;if (interfaces != null && interfaces.length > 0) {key = interfaces[0];}beanMap.put(key, obj); //类名或者接口名作为键值System.out.println(key+","+beanMap.get(key));} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}});//实现依赖注入DIbeanMap.values().stream().forEach(beanInstance -> {    Stream.of(beanInstance.getClass().getDeclaredFields()).filter(field -> field.getAnnotation(Autowired.class) != null).forEach(field -> {field.setAccessible(true); //设置属性可以访问//获取类型  keyClass<?> type = field.getType();//根据键值获取对象Object o = beanMap.get(type);try {field.set(beanInstance, o);} catch (IllegalAccessException e) {e.printStackTrace();}});});}// public static <T> T getInstance(Class<T> cls) {Object obj = beanMap.get(cls);Set<Class> classes = beanMap.keySet();for(Class c:classes){System.out.println(c+" 空格 "+beanMap.get(c));}Transaction annotation = obj.getClass().getAnnotation(Transaction.class);if (annotation == null) {return (T) obj;}return TxProxy.getProxy(obj);}

页面跳转

页面跳转类似于前端的路由,本质上都是一样的。vue项目添加路由,可以实现页面跳转。

手写了一个UImanager来控制和管理页面的跳转。

我们将页面加载分为 **懒加载和理解加载,**懒加载是只有点击页面的时候才会去加载。理解加载是程序一启动就会加载。用枚举类来标识两种加载方式。

public enum LoadType {//枚举类  立即加载 和懒加载EAGER,LAZY;
}

UIconstant也是枚举类,重写了toString方法,返回了页面的URL,给FX去加载。

public enum UIConstant {//用户页面显示Doctor(LoadType.EAGER) {@Overridepublic String toString() {return "/com/NJITZX/doctor/view/doctor.fxml";}},Doctor_ADD(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/doctor/view/addDoctor.fxml";}},Doctor_PASS(LoadType.EAGER) {@Overridepublic String toString() {return "/com/NJITZX/doctor/view/pass.fxml";}},Patient_LIST(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/patient/view/patinet.fxml";}},Doctor_UPDATE(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/doctor/view/updateDoctor.fxml";}}, DrugType_ADD(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/drug/view/drugtype.fxml";}}, Echarts_Doctor(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/doctor/view/showdata.fxml";}}, Pass_Change(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/doctor/view/chagePass.fxml";}}, Drug_ADD(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/drug/view/AddDrug.fxml";}}, Patient_Register(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/patient/view/register.fxml";}}, Chat_Init(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/chat/view/chat.fxml";}}, Patine_Diagnose(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/patient/view/diagnose.fxml";}}, Work(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/patient/view/work.fxml";}}, Pay(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/patient/view/pay.fxml";}}, Drug(LoadType.LAZY) {@Overridepublic String toString() {return "/com/NJITZX/drug/view/drug.fxml";}};private LoadType loadType;UIConstant(LoadType loadType) {this.loadType = loadType;}public LoadType getLoadType() {return loadType;}}
public class UIManger {private static Map<UIConstant,Object> nodeMap = new HashMap<>();//初始化,存入到map集合中public static void initView(){UIConstant[] valueAy = UIConstant.values();System.out.println(Arrays.toString(valueAy));for(UIConstant uiConstant:valueAy){String viewUrl = uiConstant.toString();  //得到那一串URLSystem.out.println(viewUrl);//立即加载try {if(uiConstant.getLoadType()==LoadType.EAGER) {//理解加载  fxmlloader 理解加载Parent panel = FXMLLoader.load(UIManger.class.getResource(viewUrl));nodeMap.put(uiConstant, panel);}else if(uiConstant.getLoadType()==LoadType.LAZY){//懒加载 存放到集合中去nodeMap.put(uiConstant, viewUrl);}} catch (IOException e) {e.printStackTrace();}}}//跳转页面 需要传入一个观察者列表和URLpublic static void show(ObservableList<Node> observableList,UIConstant uiConstant){observableList.clear(); //清除当前的页面if(!nodeMap.containsKey(uiConstant)){throw new RuntimeException("请先注册当前界面,没有相应fxml文件");}Node node = null;if(uiConstant.getLoadType()==LoadType.EAGER){node=(Node)nodeMap.get(uiConstant);  //理解记载直接得到对象即可}else if(uiConstant.getLoadType()==LoadType.LAZY){String viewUrl = (String)nodeMap.get(uiConstant);try {//通过反射,加载到内存node = FXMLLoader.load(UIManger.class.getResource(viewUrl));} catch (IOException e) {e.printStackTrace();}}observableList.add(node); //将node添加到观察者列表去
}

MainController 里面创建了观察者列表。 初始观察者列表是anchorpane的child组件。

public static ObservableList<Node> observableList;
this.observableList = anchorPane.getChildren();

ThreadLocal

比如我要修改医生信息,点击修改之后如何回显医生信息呢,因为我们每一个页面都是独立的,不是在一个类里面的,所以我们采用ThreadLocal线程来存放id,然后根据id找到这个医生,并回显数据。

public class BaseContext {private static ThreadLocal<Integer> DoctorLocal = new ThreadLocal<>();public static void setId(Integer id) {DoctorLocal.set(id);}public static Integer getCurId() {return DoctorLocal.get();}
}

ThreadLocal 实际开发使用的时候会有内存泄漏的问题,这个面试会经常问道。因为Map里面的那个key是强引用的,使用后不能自动回收,会让内存移除,我们需要手动的将loca移除内存。

分页查询实现

没有用工具类,手写了一个分页。

从Util-jdbc-dao-service-controller分析。

PageUtil 包含四个成员变量

currentPage  当前所在页  通过按钮不断切换
pageNum   总页数
pagesize  每一页的大小
totalcount 总记录数
package com.NJITZX.common.util;public class PageUtil {//当前页private int currentPage;//总页数private int pageNum;//每一页默认的大小private int pagesize = 4;//总共行数private int totalCount;public PageUtil() {}public PageUtil(int currentPage, int pageNum, int pagesize, int totalCount) {this.currentPage = currentPage;this.pageNum = pageNum;this.pagesize = pagesize;this.totalCount = totalCount;}public int getCurrentPage() {return currentPage;}//设置当前页数public void setCurrentPage(int currentPage) {this.currentPage = currentPage;if (currentPage < 1) {this.currentPage = 1;}if (currentPage >= this.pageNum) {this.currentPage = this.pageNum;}}public int getPageNum() {return pageNum;}//需要判断public void setPageNum() {//总页数需要 根据查询所有记录和一页的大小进行判断this.pageNum = totalCount % pagesize == 0 ? (totalCount / pagesize) : (totalCount / pagesize + 1);}public int getPagesize() {return pagesize;}public void setPagesize(int pagesize) {this.pagesize = pagesize;}public int getTotalCount() {return totalCount;}public void setTotalCount(int totalCount) {this.totalCount = totalCount;}
}

image-20240718090351990

image-20240718090358725

这两个set方法自己重写,当前页不能小于1也不能大于最大页数。

总页数,如果总行数/每一页==0,直接取值,不然需要+1。

前端需要传入分页查询的条件等,形式是键-值的形式,所以我们使用Map来存放。

   default Long count(Map<String, String> map) throws DaoException {return null;}//根据条件查询每一页的记录数default List<T> selectByCond(Map<String, String> map) throws DaoException {return null;}

在BaseDao接口里面加入default方法。 一个是返回总行数,一个是返回每一页的记录。

在模板模式jdbc里加入这两个方法;

条件查询本质就是 where 进行条件拼接。传入的sql 前面加上where 1=1 这个条件是用真的。拼接的时候and like

—即可;

Map里面的参数包括,传入的查询条件,比如logname-张三 sex-女

还有 offset 当前页 和size 每一页的大小。

    public StringBuilder dySQL(final String baseSQL, Map<String, String> mp) {StringBuilder sb = new StringBuilder(baseSQL);if (mp != null && !mp.isEmpty()) {List<String> skips = Arrays.asList("offset", "size");mp.forEach((k, v) -> {//todo//where id like CONCAT('%','1','%')//拼接查询语句 跳过offset和sizeif (!skips.contains(k) &&StringUtils.isNotEmpty(v)) {  //不是上面那个sb.append(" and ").append(k).append(" like CONCAT('%', '").append(mp.get(k)).append("','%')");}});
//            ystem.out.println(sb.toString());S}return sb;}//得到所有的count                                //条件
public long getCount(final String SQL, Map<String, String> map) throws DataException {//前面sql 是固定的 需要进行拼接  奇数的时候 拼接Connection connection = JDBCUtil.getCon();StringBuilder sb = dySQL(SQL, map);System.out.println(sb.toString());PreparedStatement pre = null;try {pre = connection.prepareStatement(sb.toString());ResultSet resultSet = pre.executeQuery();if (resultSet.next()) {return resultSet.getInt(1);//只有第一列  总行数}} catch (SQLException e) {e.printStackTrace();throw new DataException(e.getMessage());}return 0L;
}public <T> List<T> getListPage(final String SQL, Map<String, String> map, RowMapper<T> rowMapper) throws DataException {List<T> list = new ArrayList<>();Connection connection = JDBCUtil.getCon();StringBuilder sb = dySQL(SQL, map);sb.append(" limit ");  //limit 关键字sb.append(Integer.parseInt(map.get("offset")));  // offset 总当前多少行开始   需要根据当前页数和每一页的大小来计算sb.append(" , ");sb.append(Integer.parseInt(map.get("size")));  //查询的大小System.out.println(sb.toString());PreparedStatement pre = null;try {pre = connection.prepareStatement(sb.toString());ResultSet resultSet = pre.executeQuery();while (resultSet.next()) {T entity = rowMapper.mapper(resultSet);list.add(entity); //将实体添加进来}} catch (SQLException e) {e.printStackTrace();throw new DataException(e.getMessage());}return list; //返回接口}

在Dao里面进行查询

//新增加的接口
@Override
public List<DoctorVo> listByPage(Map<String, String> map) throws DaoException {String sql = "SELECT\n" +"    f.doctorid,\n" +"    f.name,\n" +"    f.gender,\n" +"    f.birthday,\n" +"    j.jobname,\n" +"    e.ename,\n" +"    d.departname,\n" +"    l.role,\n" +"    l.loginname,\n" +"    l.password\n" +"FROM t_doctor f\n" +"INNER JOIN t_login l ON f.doctorid = l.uid\n" +"INNER JOIN t_education e ON f.eid = e.eid\n" +"INNER JOIN t_jobtitle j ON f.jid = j.jobid\n" +"INNER JOIN t_department d ON f.departid = d.departid where 1=1 "; //主要where1=1;try {return jdbcTemplate.getListPage(sql, map, new DoctorVoMapper());} catch (DataException e) {e.printStackTrace();throw new DaoException(e.getMessage());}
}@Override
public Long count(Map<String, String> map) throws DaoException {String sql = "SELECT  count(1) \n" +"FROM t_doctor f\n" +"INNER JOIN t_login l ON f.doctorid = l.uid\n" +"INNER JOIN t_education e ON f.eid = e.eid\n" +"INNER JOIN t_jobtitle j ON f.jid = j.jobid\n" +"INNER JOIN t_department d ON f.departid = d.departid where 1=1";try {return jdbcTemplate.getCount(sql, map);} catch (DataException e) {e.printStackTrace();throw new DaoException(e.getMessage());}
}

Serivce层

 // offset  -->page 页数// size 每一页的显示数目@Overridepublic List<DoctorVo> listByPage(Map<String, String> map) throws ServiceException {long page = 0L; //当前页long size = 0L; //每一页记录数if (map.containsKey("page")) { //当前页page = Long.parseLong(map.get("page"));map.remove("page");}if (map.containsKey("size")) {size = Long.parseLong(map.get("size"));}long offset = (page - 1) * size; //从(page-1)*size开始  就是sql语句忽略的行数开始map.put("offset", String.valueOf(offset));//获取每一页记录数try {List<DoctorVo> doctorVos = doctorInfoDao.listByPage(map);return  doctorVos;} catch (DaoException e) {e.printStackTrace();throw new  ServiceException(e.getMessage());}}@Overridepublic long countInfo(Map<String, String> map) throws ServiceException {Long count = null;try {count = doctorInfoDao.count(map);} catch (DaoException e) {e.printStackTrace();throw new ServiceException(e.getMessage());}return count;}

Controller层

首页 上一页 下一页 尾页 都需要绑定一个action,点击切换对应的页面。

image-20240718091806293

统计模块

统计这一块学习了JDK的Stream流和lamda表达式来进行数据统计。

image-20240718184424863

 public PieChart echarts; //创建的饼状图public DrugTypeService drugTypeService =BeanFactory.getInstance(DrugTypeService.class);public void loadSexEcharts() {ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList();try {Stream<Drug> drugs = drugService.listAll().stream();//根据性别进行分组  stream流Map<Integer, List<Drug>> fenzu =        drugs.collect(Collectors.groupingBy(Drug::getDrugType));Set<Integer> integers = fenzu.keySet();//这一块还可以进行优化for (Integer c : integers) {//按照类别分组后List<Drug> drug = fenzu.get(c);int amount = 0;for (Drug d : drug) {amount += d.getAmount();}//根据id得到类别DrugType drugType = drugTypeService.getDrugType(c);//向观察者列表添加数据pieChartData.add(new PieChart.Data(drugType.getCname(), amount));}} catch (ServiceException e) {e.printStackTrace();}echarts.setData(pieChartData);echarts.setClockwise(true);echarts.setPrefHeight(400);echarts.setPrefWidth(400);echarts.setTitle("各种类别药品比列");}

聊天

这一块还没有做完整。

想要展示当前在线医生,可以在login上面添加一个字段,当医生登录或者退出的的时候会改变这个字段。

聊天目前采用的方式想是通过time定时器来实现定时轮询,但是线程这一块报异常,应该是前面数据库连接采用了单列模式,出现了一些问题。
在这里插入图片描述

同时给出其他的方案。

1.采用websocket+netty进行消息推送,当后端接口了消息的时候,根据消息的接收方推送消息到前端来,这个会在后面用boot框架去做

2.采用socket网络编程,实现统一局域网内的同学,建立一个服务端,多个医生是客户端,服务端存储和转发消息。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【力扣】最小栈
  • 【Unity C#基础】浅谈List底层逻辑
  • npm相关指令
  • 现场可重构CPLD芯片应用案例—蓝牙音箱
  • PHP 与 1688 详情 API 接口的完美对接
  • UE4-初见虚幻引擎
  • java包装类 及其缓存
  • 浏览器的缓存
  • PyCharm软件初始化配置
  • 使用 Flask 3 搭建问答平台(三):注册页面模板渲染
  • 华为OD机考题(基础API)
  • 【Linux】服务器安装SSH
  • 人工智能算法工程师(中级)课程14-神经网络的优化与设计之拟合问题及优化与代码详解
  • 通过docker构建基于LNMP的WordPress项目
  • 微信小程序基本语法
  • es6--symbol
  • Spring Boot MyBatis配置多种数据库
  • Spring Cloud Feign的两种使用姿势
  • Vue.js 移动端适配之 vw 解决方案
  • 程序员最讨厌的9句话,你可有补充?
  • 利用jquery编写加法运算验证码
  • 排序(1):冒泡排序
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 译有关态射的一切
  • 移动端高清、多屏适配方案
  • ​学习笔记——动态路由——IS-IS中间系统到中间系统(报文/TLV)​
  • #70结构体案例1(导师,学生,成绩)
  • #HarmonyOS:基础语法
  • #pragma once
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (003)SlickEdit Unity的补全
  • (4)事件处理——(7)简单事件(Simple events)
  • (5)STL算法之复制
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (十三)MipMap
  • (实战篇)如何缓存数据
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转)程序员疫苗:代码注入
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .NET COER+CONSUL微服务项目在CENTOS环境下的部署实践
  • .NET CORE 3.1 集成JWT鉴权和授权2
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • .net网站发布-允许更新此预编译站点
  • .net中应用SQL缓存(实例使用)
  • /dev下添加设备节点的方法步骤(通过device_create)
  • @font-face 用字体画图标
  • [3D基础]理解计算机3D图形学中的坐标系变换
  • [bzoj1901]: Zju2112 Dynamic Rankings
  • [caffe(二)]Python加载训练caffe模型并进行测试1
  • [CSS]文字旁边的竖线以及布局知识
  • [GYCTF2020]Ez_Express
  • [hdu 3746] Cyclic Nacklace [kmp]
  • [javaSE] GUI(Action事件)
  • [JavaWeb玩耍日记]Maven的安装与使用