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网络编程,实现统一局域网内的同学,建立一个服务端,多个医生是客户端,服务端存储和转发消息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1483802.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Linux如何正确安装MySQL数据库

对于Linux安装mysql,如果大家有不会的可以来参考小编的详细安装步骤哦&#xff0c;小编带你一步步走向成功~ 首先对于Linux系统&#xff0c;我们通过小编的上一篇文章中知道安装软件的命令为wget&#xff0c;所以首先需要写出命令获取mysql&#xff1a; wget https://cdn.mys…

高频面试题-CSS

BFC 介绍下BFC (块级格式化上下文) 1>什么是BFC BFC即块级格式化上下文&#xff0c;是CSS可视化渲染的一部分, 它是一块独立的渲染区域&#xff0c;只有属于同一个BFC的元素才会互相影响&#xff0c;且不会影响其它外部元素。 2>如何创建BFC 根元素&#xff0c;即HTM…

【Git远程操作】向远程仓库推送 | 拉取远程仓库

目录 1.向远程仓库推送 ​1.1本地仓库的配置 1.2remote-gitcode本地仓库 1.3推送至远程仓库 2.拉取远程仓库 现阶段以下操作仅在master主分支上。 1.向远程仓库推送 工作区☞add☞暂存区☞commit☞本地仓库☞推送push☞远程仓库注意&#xff1a;本地仓库的某个分支 ☞推…

趣谈linux操作系统 9 网络系统-读书笔记

文章目录 网络协议栈基础知识回顾网络分层网络分层的目的各层作用简介延伸-ip地址,有类,无类,cidr socket实现分析tcp/udp回顾socket编程回顾TCP编程回顾UDP编程回顾差异 socket相关接口实现浅析sokcet实现解析创建socket的三个参数socket函数定义及其参数创建socket结构体关联…

小白新手搭建个人网盘

小白新手搭建个人网盘 序云服务器ECS重置密码远程连接ECS实例 安装OwnCloud安装Apache服务PHP运行环境NAS挂载挂载验证操作体验 序 阿里云文件存储NAS&#xff08;Apsara File Storage NAS&#xff09;是一个可大规模共享访问&#xff0c;弹性扩展的分布式文件系统。本文主要是…

(三)原生js案例之滚动到底部解锁按钮状态

业务主要是注册页面&#xff0c;有很长的条款需要用户去读&#xff0c;必须确认用户是不是看到全部的条款&#xff0c;这个场景下可以使用 效果 代码实现 必要的css <style>*{padding: 0;margin: 0;}ul{list-style: none;width: 330px;height: 100%;/* height: 200px;…

Adobe国际认证详解-影视后期

在当今的数字媒体时代&#xff0c;影视后期制作作为创意产业的核心环节&#xff0c;对于专业技能的要求日益提高。Adobe国际认证&#xff0c;作为全球创意设计领域的重要标杆&#xff0c;为影视后期制作人员提供了一个展示自我、提升技能的国际舞台。 何为影视后期&#xff1f;…

在学习使用LabVIEW的过程中,需要注意哪些问题?

在学习使用LabVIEW的过程中&#xff0c;需要注意以下问题&#xff1a; 1. 基础知识 图形化编程思维&#xff1a; LabVIEW采用图形化编程方式&#xff0c;与传统的文本编程语言有很大不同&#xff0c;需要适应这种新的编程思维方式。数据流概念&#xff1a; 理解LabVIEW的核心数…

【Git标签管理】理解标签 | 创建标签 | 查看标签 | 删除标签 | 推送标签

目录 1.理解标签 2.创建标签 3.查看标签 4.删除本地仓库的标签 5.推送标签 6.删除远程仓库的标签 1.理解标签 Git提供一个打标签的功能tag&#xff0c;对某一次事务/提交的表示&#xff08;作用/意义&#xff09;。标签 tag &#xff0c;可以简单的理解为是对某次 comm…

免费的数字孪生平台助力产业创新,让新质生产力概念有据可依

关于新质生产力的概念&#xff0c;在如今传统企业现代化发展中被反复提及。 那到底什么是新质生产力&#xff1f;它与哪些行业存在联系&#xff0c;我们又该使用什么工具来加快新质生产力的发展呢&#xff1f;今天我将介绍一款为发展新质生产力而量身定做的数字孪生工具。 新…

Ubuntu 24.04 LTS 桌面安装MT4或MT5 (MetaTrader)教程

运行脚本即可在 Ubuntu 24.04 LTS Noble Linux 上轻松安装 MetaTrader 5 或 4 应用程序&#xff0c;使用 WineHQ 进行外汇交易。 MetaTrader 4 (MT4) 或 MetaTrader 5 是用于交易外汇对和商品的流行平台。它支持各种外汇经纪商、内置价格分析工具以及通过专家顾问 (EA) 进行自…

酷炫末世意境背景404单页HTML源码

源码介绍 酷炫末世意境背景404单页HTML源码&#xff0c;背景充满着破坏一切的意境&#xff0c;彷佛末世的到来&#xff0c;可以做网站错误页或者丢失页面&#xff0c;将下面的代码放到空白的HTML里面&#xff0c;然后上传到服务器里面&#xff0c;设置好重定向即可 效果预览 …

PyTorch 深度学习实践-循环神经网络(高级篇)

视频指路 参考博客笔记 参考笔记二 文章目录 上课笔记总代码练习 上课笔记 个人能力有限&#xff0c;重看几遍吧&#xff0c;第一遍基本看不懂 名字的每个字母都是一个特征x1,x2,x3…&#xff0c;一个名字是一个序列 rnn用GRU 用ASCII表作为词典&#xff0c;长度为128&#x…

OceanBase:引领下一代分布式数据库技术的前沿

OceanBase的基本概念 定义和特点 OceanBase是一款由蚂蚁金服开发的分布式关系数据库系统&#xff0c;旨在提供高性能、高可用性和强一致性的数据库服务。它结合了关系数据库和分布式系统的优势&#xff0c;适用于大规模数据处理和高并发业务场景。其核心特点包括&#xff1a; …

源码分析SpringCloud Gateway如何加载断言(predicates)与过滤器(filters)

我们今天的主角是Gateway网关&#xff0c;一听名字就知道它基本的任务就是去分发路由。根据不同的指定名称去请求各个服务&#xff0c;下面是Gateway官方的解释&#xff1a; Spring Cloud Gateway&#xff0c;其他的博主就不多说了&#xff0c;大家多去官网看看&#xff0c;只…

<数据集>蛋壳裂缝检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;2520张 标注数量(xml文件个数)&#xff1a;2520 标注数量(txt文件个数)&#xff1a;2520 标注类别数&#xff1a;2 标注类别名称&#xff1a;[crack, egg] 序号类别名称图片数框数1crack245128352egg25142514 使…

最新 Docker 下载镜像超时解决方案:Docker proxy

现在Docker换源也下载失败太常见了&#xff0c;至于原因&#xff0c;大家懂得都懂。本文提供一种简洁的方案&#xff0c; 利用 Docker 的http-proxy&#xff0c;代理至本机的 proxy。 文章目录 前言Docker proxy 前言 这里默认你会安装 clash&#xff0c;然后有配置和数据库。…

LLM大模型实战项目--基于Stable Diffusion的电商平台虚拟试衣

本文详细讲解LLM大模型实战项目&#xff0c;基于Stable Diffusion的电商平台虚拟试衣 一、项目介绍 二、阿里PAI平台介绍 三、阿里云注册及开通PAI 四、PAI_DSW环境搭建 五、SDLORA模型微调 一、项目介绍 AI虚拟试衣是一种创新的技术&#xff0c;利用人工智能和计算机视觉技…

科技核心~书法用纸结合

书法用纸******对墨迹扩散的影响 传统书法用纸制作****与现代改进 书法用纸的*****表面结构关系研究

ARM架构(二)—— arm v7/v8/v9寄存器介绍

1、ARM v7寄存器 1.1 通用寄存器 V7 V8开始 FIQ个IRQ优先级一样&#xff0c; 通用寄存器&#xff1a;31个 1.2 程序状态寄存器 CPSR是程序状态毒存器&#xff0c;保存条件标志位&#xff0c;中断禁止位&#xff0c;当前处理器模式等控制和状态位。每种异常模式下还存在SPSR&…