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://xiahunao.cn/news/3248841.html

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

相关文章

R语言进行集成学习算法:随机森林

# 10.4 集成学习及随机森林 # 导入car数据集 car <- read.table("data/car.data",sep ",") # 对变量重命名 colnames(car) <- c("buy","main","doors","capacity","lug_boot","safety"…

ARM体系结构和接口技术(五)封装RCC和GPIO库

文章目录 一、RCC&#xff08;一&#xff09;思路1. 找到时钟基地址2. 找到总线的地址偏移&#xff08;1&#xff09;AHB4总线&#xff08;2&#xff09;定义不同GPIO组的使能宏函数&#xff08;3&#xff09;APB1总线&#xff08;4&#xff09;定义使能宏函数 二、GPIO&#x…

基于Java的汽车租赁管理系统设计(含文档、源码)

本篇文章论述的是基于Java的汽车租赁管理系统设计的详情介绍&#xff0c;如果对您有帮助的话&#xff0c;还请关注一下哦&#xff0c;如果有资源方面的需要可以联系我。 目录 摘 要 系统运行截图 系统总体设计 系统论文 资源下载 摘 要 近年来&#xff0c;随着改革开放…

React遍历tree结构,获取所有的id,切换自动展开对应层级

我们在做一个效果的时候&#xff0c;经常可能要设置默认展开多少的数据 1、页面效果&#xff0c;切换右侧可以下拉可切换展开的数据层级&#xff0c;仅展开两级等 2、树形的数据

C语言中常见库函数(1)——字符函数和字符串函数

文章目录 前言1.字符分类函数2.字符转换函数3.strlen的使用和模拟实现4.strcpy的使用和模拟实现5.strcat的使用和模拟实现6.strncmp的使用和模拟实现7.strncpy函数的使用8.strncat函数的使用9.strncmp函数的使用10.strstr的使用和模拟实现11.strtok函数的使用12.strerror函数的…

【文献阅读】Social Bot Detection Based on Window Strategy

Abstract 机器人发帖的目的是在不同时期宣传不同的内容&#xff0c;其发帖经常会出现异常的兴趣变化、而人类发帖的目的是表达兴趣爱好和日常生活&#xff0c;其兴趣变化相对稳定。提出了一种基于窗口策略&#xff08;BotWindow Strategy&#xff09;的社交机器人检测模型基于…

深入了解MySQL文件排序

数据准备 CREATE TABLE user_info (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT ID,name varchar(20) NOT NULL COMMENT 用户名,age tinyint(4) NOT NULL DEFAULT 0 COMMENT 年龄,sex tinyint(2) NOT NULL DEFAULT 0 COMMENT 状态 0&#xff1a;男 1&#xff1a; 女,creat…

R语言实现对模型的参数优化与评价KS曲线、ROC曲线、深度学习模型训练、交叉验证、网格搜索

目录 一、模型性能评估 1、数据预测评估 2、概率预测评估 二、模型参数优化 1、训练集、验证集、测试集的引入 2、k折线交叉验证 2、网格搜索 一、模型性能评估 1、数据预测评估 ### 数据预测评估 #### 加载包&#xff0c;不存在就进行在线下载后加载if(!require(mlben…

NFS存储、API资源对象StorageClass、Ceph存储-搭建ceph集群和Ceph存储-在k8s里使用ceph(2024-07-16)

一、NFS存储 注意&#xff1a;在做本章节示例时&#xff0c;需要拿单独一台机器来部署NFS&#xff0c;具体步骤略。NFS作为常用的网络文件系统&#xff0c;在多机之间共享文件的场景下用途广泛&#xff0c;毕竟NFS配置方 便&#xff0c;而且稳定可靠。NFS同样也有一些缺点&…

《Towards Black-Box Membership Inference Attack for Diffusion Models》论文笔记

《Towards Black-Box Membership Inference Attack for Diffusion Models》 Abstract 识别艺术品是否用于训练扩散模型的挑战&#xff0c;重点是人工智能生成的艺术品中的成员推断攻击——copyright protection不需要访问内部模型组件的新型黑盒攻击方法展示了在评估 DALL-E …

昇思25天训练营Day18 - 基于MobileNetv2的垃圾分类

基于MobileNetv2的垃圾分类 本文档主要介绍垃圾分类代码开发的方法。通过读取本地图像数据作为输入&#xff0c;对图像中的垃圾物体进行检测&#xff0c;并且将检测结果图片保存到文件中。 1、实验目的 了解熟悉垃圾分类应用代码的编写&#xff08;Python语言&#xff09;&a…

[MySQL][复核查询][多表查询][自连接][自查询]详细讲解

目录 1.铺垫&基本查询回顾1.多表查询1.何为笛卡尔积&#xff1f;2.示例 2.自连接1.何为自连接&#xff1f;2.示例 3.子查询1.何为子查询&#xff1f;2.单行子查询3.多行子查询4.多列子查询5.在from子句中使用子查询6.合并查询 1.铺垫&基本查询回顾 前面讲解的MYSQL表的…

【深度学习入门篇 ⑨】循环神经网络实战

【&#x1f34a;易编橙&#xff1a;一个帮助编程小伙伴少走弯路的终身成长社群&#x1f34a;】 大家好&#xff0c;我是小森( &#xfe61;ˆoˆ&#xfe61; ) &#xff01; 易编橙终身成长社群创始团队嘉宾&#xff0c;橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官…

运算符的使用

一、运算符介绍 运算符是一种特殊的符号&#xff0c;用以表示数据的运算、赋值和比较等 算术运算符赋值运算符比较运算符逻辑运算符位运算符 二、算术运算符 1、算术运算符是对数值类型的变量进行运算的&#xff0c;在程序中使用的非常多 2、算术运算符的使用 # 算术运算符…

Learning vtkjs之vtkSource

vtkSource的主要类型 Cone 锥体Circle 圆形Arrow 箭头ConcentricCylinder 同心圆Cube 方形Cursor3D 包围盒Cylinder 圆柱体Line 线Plane 平面Point 点Sphere 球不能调整center的source 目前整理的有下面几种source&#xff0c;对应有点类似threejs的mesh&#xff0c;通过一定的…

【.NET全栈】ASP.NET开发Web应用——站点导航技术

文章目录 前言一、站点地图1、定义站点地图文件2、使用SiteMapPath控件3、SiteMap类4、URL地址映射 二、TreeView控件1、使用TreeView控件2、以编程的方式添加节点3、使用TreeView控件导航4、绑定到XML文件5、按需加载节点6、带复选框的TreeView控件 三、Menu控件1、使用Menu控…

C语言指针超详解——进阶篇

C语言指针系列文章目录 入门篇 强化篇 进阶篇 文章目录 C语言指针系列文章目录1. 字符指针变量2. 数组指针变量2. 1 概念2. 2 数组指针变量的初始化 3. 二维数组传参的本质4. 函数指针变量4. 1 函数指针变量的创建4. 2 指针变量的使用4. 3 两个有趣的代码4. 3. 1 代码一4. 3. …

c++初阶知识——内存管理与c语言内存管理对比

目录 前言&#xff1a; 1.c&#xff0b;&#xff0b;内存管理方式 1.1 new和delete操作自定义类型 2.operator new与operator delete函数 2.1 operator new与operator delete函数 3.new和delete的实现原理 3.1 内置类型 3.2 自定义类型 new的原理 delete的原理 new…

完整教程 linux下安装百度网盘以及相关依赖库,安装完成之后启动没反应 或者 报错

完整教程 linux下安装百度网盘以及相关依赖库&#xff0c;安装完成之后启动没反应 或者 报错。 配置国内镜像源&#xff1a; yum -y install wget mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak wget -O /etc/yum.repos.d/CentOS-Base.repo ht…

数据库端口LookUp功能:从数据库中获取并添加数据到XML

本文将为大家介绍如何使用知行之桥EDI系统数据库端口的Lookup功能&#xff0c;从数据库中获取数据&#xff0c;并添加进输入的XML中。 使用场景&#xff1a;期待以输入xml中的值为判断条件从数据库中获取数据&#xff0c;并添加进输入xml中。 例如&#xff1a;接收到包含采购…