十、ManyToOne的实现

本文详细介绍了如何使用BDBJE对包含OneToMany关系的数据进行存储,包括部门和员工的关系建模、索引创建、实体获取及更新等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BDB JE对复杂数据的储存 (三)、ManyToOne关系的存储

 


部门类:

 @Entity

public class Department {

    @PrimaryKey

    int departmentId;

    @SecondaryKey(relate=Relationship.ONE_TO_ONE)

    String departmentName;

    String location;

    public Department(int departmentId,String departmentName,String location){

       this.departmentId=departmentId;

       this.departmentName=departmentName;

       this.location=location;

    }

    Department(){ }

}

员工类:

@Entity

public class Employee {

    @PrimaryKey

    int employeeId;

    @SecondaryKey(relate=Relationship.MANY_TO_ONE)

    String employeeName;

    @SecondaryKey(relate=Relationship.MANY_TO_ONE)

    float salary;

@SecondaryKey

(relate=Relationship.MANY_TO_ONE,relatedEntity=Employee.class,onRelatedEntityDelete=DeleteActio

n.NULLIFY)

    Integer managerId;

@SecondaryKey

(relate=Relationship.MANY_TO_ONE,relatedEntity=Department.class,onRelatedEntityDelete=DeleteAct

ion.CASCADE)

    Integer departmentId;

    String address;

    public Employee(int employeeId,String employeeName,float salary,Integer managerId,int

departmentId,String address){

       this.employeeId=employeeId;

       this.employeeName=employeeName;

       this.salary=salary;

       this.managerId=managerId;

       this.departmentId=departmentId;

       this.address=address;

    }

    public Employee(){}

}

操作类:

public class Accessor {

    PrimaryIndex<Integer, Employee> employeeById;

    SecondaryIndex<String, Integer, Employee> employeeByName;

    SecondaryIndex<Float, Integer, Employee> employeeBySalary;

    SecondaryIndex<Integer, Integer, Employee> employeeByManagerId;

    SecondaryIndex<Integer, Integer, Employee> employeeByDepartmentId;

    PrimaryIndex<Integer, Department> departmentById;

    SecondaryIndex<String, Integer, Department> departmentByName;

    public Accessor(EntityStore store) throws DatabaseException{

       employeeById=store.getPrimaryIndex(Integer.class, Employee.class);

       employeeByName=store.getSecondaryIndex(employeeById, String.class, "employeeName");

       employeeBySalary=store.getSecondaryIndex(employeeById, Float.class, "salary");

       employeeByManagerId=store.getSecondaryIndex(employeeById, Integer.class, "managerId");

       employeeByDepartmentId=store.getSecondaryIndex(employeeById, Integer.class,

"departmentId");

       departmentById=store.getPrimaryIndex(Integer.class, Department.class);

       departmentByName=store.getSecondaryIndex(departmentById, String.class,

"departmentName");

    }

}

public class test {

 

    public static void main(String[] args) {

       EnvironmentConfig envConfig = new EnvironmentConfig();

       envConfig.setAllowCreate(true);

       envConfig.setTransactional(true);

       try {

           Environment env = new Environment(new File("d://bdb//many2oneje2"),

                  envConfig);

           StoreConfig storeConfig = new StoreConfig();

           storeConfig.setAllowCreate(true);

           storeConfig.setTransactional(true);

           EntityStore store = new EntityStore(env, "EmployeeStore",

                  storeConfig);

           Accessor dao = new Accessor(store);

           Transaction txn = env.beginTransaction(null, null);

 

             dao.departmentById.put(txn,new

             Department(1,"CEO Office","North America"));

             dao.departmentById.put(txn,new Department(2,"Sales","EURO"));

             dao.departmentById.put(txn,new Department(3,"HR","MEA"));

             dao.departmentById.put(txn,new

             Department(4,"Engineering","APAC"));

             dao.departmentById.put(txn,new Department(5,"Support","LATAM"));

             dao.employeeById.put(txn,new

             Employee(1,"Abraham Lincoln",1000.0f,

             null,1,"Washington D.C.,USA")); dao.employeeById.put(txn,new

             Employee(2,"Augustus",9000.0f,1,2,"Rome,Italy"));

             dao.employeeById.put(txn,new

             Employee(3,"Cleopartra",7000.0f,1,3,"Cairo,Egypt"));

             dao.employeeById.put(txn,new

             Employee(4,"Confucius",7500.0f,1,4,"Beijing,China"));

             dao.employeeById.put(txn,new

             Employee(5,"Toussaint Louverture",6800.0f

             ,1,5,"Port-au-prince,Haiti")); dao.employeeById.put(txn,new

             Employee(6,"William Shakespeare",7300.0f,2,2,"London,Englad"));

             dao.employeeById.put(txn,new

             Employee(7,"Vistor Hugo",7000.0f,2,2,"paris,France"));

             dao.employeeById.put(txn,new

             Employee(8,"Yitzhak Rabin",6500.0f,3,3,"Jerusalem,Israel"));

             dao.employeeById.put(txn,new

             Employee(9,"Nelson Rolihlahla Mandela"

             ,6400.0f,3,3,"Cape Town,South Africa"));

             dao.employeeById.put(txn,new

             Employee(10,"Mei ji Emperor",6600.0f,4,4,"Tokyo,Japan"));

             dao.employeeById.put(txn,new

             Employee(11,"Mohandas Karamchand Gandhi"

             ,7600.0f,4,4,"New Delhi,India")); dao.employeeById.put(txn,new

             Employee

             (12,"Ayrton Senna da Silva",5600.0f,5,5,"Brasilia ,Brasil"));

             dao.employeeById.put(txn,new

             Employee(13,"Ronahlinho De Assis Moreiar"

             ,6100.0f,5,5,"Brasilia ,Brasil"));

           

 

           // 通过游标得到实体

           System.out.println("得到部门的信息,其按ID排序,升序");

           EntityCursor<Department> deptcursor = dao.departmentById.entities(

                  txn, null);

           for (Department dept : deptcursor) {

              System.out.println(dept.departmentId + "     "

                     + dept.departmentName);

           }

           deptcursor.close();

 

           System.out.println("得到部门的信息,其按部门名称排序,升序");

           deptcursor = dao.departmentByName.entities(txn, null);

           for (Department dept : deptcursor) {

              System.out.println(dept.departmentId + "     "

                     + dept.departmentName);

           }

           deptcursor.close();

           System.out.println("******************************");

           // 通过get得到实体

           System.out.println("得到部门id为1的信息");

           Department d = dao.departmentById.get(txn, 1, LockMode.DEFAULT);

           System.out.println(d.departmentId + "   " + d.departmentName);

           System.out.println("--------/////////////////////------");

           // 通过关键字

           System.out.println("得到部门名称为sales的信息");

           d = dao.departmentByName.get(txn, "Sales", LockMode.DEFAULT);

           if (d != null) {

              System.out.println(d.departmentId + "   " + d.departmentName);

           }

           System.out.println("-------------------");

           // 限制游标的范围

           // 第一个参数代表为开始位置,按ACSII码查询,第二个参数代表(trur:>= false:>)

           // 第三个参数代表为结束位置,按ACSII码结束,其为一个字符的比较,第四个为(true:<=

false:<)

           deptcursor = dao.departmentByName.entities(txn, "C", true, "Sales",

                  true, null);

           for (Department dept : deptcursor) {

              System.out.println(dept.departmentName);

           }

           deptcursor.close();

 

           System.out.println("----------------------------");

 

           deptcursor = dao.departmentByName.subIndex("Sales").entities(txn,

                  null);

           for (Department dept : deptcursor) {

              System.out.println(dept.departmentId + "    "

                     + dept.departmentName);

           }

           deptcursor.close();

 

           System.out

                  .println("得到部门名为Sales,其ID为2的部门信息(如果get中为其他的值的时候都会报

错,因为部门名为Sales的部门ID为2)");

           d = dao.departmentByName.subIndex("Sales").get(txn, 2,

                  LockMode.DEFAULT);

           if (d != null) {

              System.out.println(d.departmentId + "   " + d.departmentName);

           }

           // dao.departmentById.delete(1);

           System.out

                  .println("////////////////////////////////////////////////");

           // 得到第一个部门的名称

           d = dao.departmentById.get(txn, 1, LockMode.DEFAULT);

           System.out.println(d.departmentName);

           System.out

                  .println("************************************************");

           // 得到部门ID为1的第一个员工的名字

           System.out.println("得到部门ID为2的第一个员工的名字:");

           System.out.println(dao.employeeByDepartmentId.get(txn, 2,

                  LockMode.DEFAULT).employeeName);

           System.out.println("+++++++++++++++++++++++++++++++++++++");

 

           System.out.println("查询工资在6000-8000之间的所有员工;");

           EntityCursor<Employee> ecursor = dao.employeeBySalary.entities(txn,

                  new Float(6000), true, new Float(8000), true, null);

           for (Employee e : ecursor) {

               System.out.println(e.employeeId + "    " + e.salary);

           }

           ecursor.close();

           System.out.println("-------------------");

           // 得到上司的编号为2的第一个员工的名称

           System.out.println("得到上司的编号为2的第一个员工的名称:");

           System.out.println(dao.employeeByManagerId.get(txn, 2,

                  LockMode.DEFAULT).employeeName);

           System.out

                  .println("------------------------------------------------------------------

---");

 

           // 查询公司的所有的员工

           System.out.println("查询公司的所有的员工:");

           System.out.println("编号    " + "名称" + "    " + "工资" + "   " + "地址"

                  + "      " + "部门ID" + "   " + "上司ID");

           EntityCursor<Employee> employeeCursor = dao.employeeById.entities(

                  txn, null);

 

           for (Employee e : employeeCursor) {

              System.out.println(e.employeeId + "  " + e.employeeName + " "

                     + e.salary + " " + e.address + "   " + e.departmentId

                     + "   " + e.managerId);

           }

           employeeCursor.close();

           System.out.println("---------------------");

           // 查询上司编号为1,并且工作为7000.0f的员工

           System.out.println("查询上司编号为1,并且工作为7000.0f的员工:");

 

           EntityJoin<Integer, Employee> entityJoin = new EntityJoin(

                  dao.employeeById);

           entityJoin.addCondition(dao.employeeByManagerId, 1);

           entityJoin.addCondition(dao.employeeBySalary, 7000.0f);

 

           ForwardCursor<Employee> c = entityJoin.entities(txn, null);

           for (Employee e : c) {

              System.out.println(e.employeeId + "   " + e.employeeName

                     + "   " + e.salary + "      " + e.managerId);

           }

           c.close();

           System.out.println("===========================================");

 

           // 联合查询(同一部门中的所有人员)

           System.out.println("部门名为Support中的所有人员");

           Department department = dao.departmentByName.get(txn, "Support",

                  LockMode.DEFAULT);

           if (department != null) {

              EntityCursor<Employee> ec = dao.employeeByDepartmentId

                     .subIndex(department.departmentId).entities(txn, null);

 

              for (Employee emp : ec) {

                  System.out.println(emp.employeeId + "   "

                         + emp.employeeName + "   " + emp.address);

              }

              ec.close();

           }

           System.out.println("相应的部门中所有员工显示:");

           EntityCursor<Department> deptc = dao.departmentById.entities(txn,

                  null);

           for (Department dd : deptc) {

              EntityCursor<Employee> eec = dao.employeeByDepartmentId

                     .subIndex(dd.departmentId).entities(txn, null);

              for (Employee emp : eec) {

                  System.out.println(emp.employeeId + "   "

                         + emp.employeeName + "   " + emp.address + "   "

                         + dd.departmentName);

              }

              eec.close();

           }

           deptc.close();

           System.out

                  .println("删除部门名为:Support,由于用到了CASCADE,在删除部门时,会把对应的部门

里的员工也会删除掉");

           deptc = dao.departmentById.entities(txn, null);

           for (Department dd : deptc) {

              if (dd.departmentName.trim().equals("Support")) {

                  // 最好用游标来进行删除和修改的操作

                  deptc.delete();

              }

              if (dd.departmentName.trim().equals("Sales")) {

                  dd.departmentName = dd.departmentName.toUpperCase();

                  deptc.update(dd);

              }

           }

           deptc.close();

           System.out.println("相应的部门中所有员工显示:");

           deptc = dao.departmentById.entities(txn, null);

           for (Department dd : deptc) {

              EntityCursor<Employee> eec = dao.employeeByDepartmentId

                     .subIndex(dd.departmentId).entities(txn, null);

              for (Employee emp : eec) {

                  System.out.println(emp.employeeId + "   "

                         + emp.employeeName + "   " + emp.address + "   "

                         + dd.departmentName);

              }

               eec.close();

           }

           deptc.close();

           store.close();

           txn.commit();

           env.close();

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

}

 

<think>好的,我需要帮助用户实现使用SpringBoot和Vue实现文件(Word或PDF)按章节拆分并存储到数据库的功能,并提供查询接口的示例代码。首先,我需要理清整个流程,分步骤分析。 首先,后端部分使用SpringBoot处理文件上传和拆分。用户上传的可能是Word或PDF文件,所以需要相应的解析库。对于Word文档,Apache POI是一个常用的库,可以处理.docx文件。而PDF文件的话,可能需要使用Apache PDFBox来提取文本内容。然后,按章节拆分需要识别章节标题,可能需要正则表达式来匹配章节标题模式,比如“第X章”或“Chapter X”等。拆分后的章节内容需要存储到数据库,这里可能需要设计一个数据库表,包含章节标题、内容、所属文件等信息。同时,文件本身的信息也需要存储,比如文件名、上传时间等,所以可能需要两个实体类:FileEntity和ChapterEntity,它们之间是一对的关系。 接下来,存储到数据库,可以使用Spring Data JPA或者MyBatis。考虑到示例代码的简洁性,可能JPA更合适,因为它可以通过注解简化ORM配置。然后,需要提供RESTful API来处理文件上传,拆分后的数据存储,以及查询接口。文件上传可以使用MultipartFile接收,处理完成后返回相应的结果。 前端部分使用Vue,需要实现文件上传组件,使用axios发送文件到后端接口。上传成功后,可能需要展示上传的文件列表,以及提供查询功能,比如根据文件名或章节标题搜索。前端还需要处理可能的错误,比如文件格式不支持或上传失败的情况。 在代码实现上,后端需要创建FileUploadController,处理上传请求,调用解析和拆分服务。解析服务需要根据文件类型调用不同的解析器,比如WordParser和PDFParser。拆分逻辑需要统一处理,提取章节并保存到数据库。数据库方面,配置数据源,定义实体和Repository接口。 可能遇到的问题包括文件解析的准确性,特别是PDF中的格式处理,以及章节拆分的正则表达式是否足够健壮。此外,大文件的上传和处理可能需要考虑性能问题,比如异步处理或者分块上传,但示例代码可能暂时不考虑这些复杂情况,先实现基本功能。 测试时,需要确保不同格式的文件都能正确解析,章节拆分准确,存储到数据库的数据完整,查询接口返回正确的结果。前端需要测试文件上传的交互是否流畅,查询功能是否正常。 最后,整合前后端,确保跨域问题解决,接口通信正常。可能需要配置CORS,或者在Vue的devServer中设置代理,避免跨域错误。 总结下来,步骤是:设计数据库模型→实现文件解析和拆分逻辑→创建SpringBoot控制器处理API→配置前端Vue组件处理上传和查询→测试整个流程。</think>以下是基于SpringBoot+Vue实现文件分章节存储查询功能的完整解决方案: # 一、后端实现(SpringBoot) 1. 数据库表结构设计 ```sql CREATE TABLE uploaded_file ( id BIGINT PRIMARY KEY AUTO_INCREMENT, file_name VARCHAR(255) NOT NULL, file_type VARCHAR(10) NOT NULL, upload_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE chapter ( id BIGINT PRIMARY KEY AUTO_INCREMENT, file_id BIGINT NOT NULL, chapter_title VARCHAR(255) NOT NULL, content TEXT NOT NULL, FOREIGN KEY (file_id) REFERENCES uploaded_file(id) ); ``` 2. 实体类定义 ```java // FileEntity.java @Entity public class FileEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String fileName; private String fileType; private LocalDateTime uploadTime; @OneToMany(mappedBy = "file", cascade = CascadeType.ALL) private List<Chapter> chapters = new ArrayList<>(); } // Chapter.java @Entity public class Chapter { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String chapterTitle; private String content; @ManyToOne @JoinColumn(name = "file_id") private FileEntity file; } ``` 3. 文件解析服务(关键代码) ```java @Service public class FileParserService { // 解析Word文档 public List<Chapter> parseWord(MultipartFile file) throws Exception { XWPFDocument doc = new XWPFDocument(file.getInputStream()); List<Chapter> chapters = new ArrayList<>(); StringBuilder currentContent = new StringBuilder(); String currentTitle = "无标题"; for (IBodyElement elem : doc.getBodyElements()) { if (elem instanceof XWPFParagraph) { XWPFParagraph para = (XWPFParagraph) elem; String text = para.getText().trim(); if (isChapterTitle(text)) { // 使用正则判断章节标题 if (currentContent.length() > 0) { chapters.add(new Chapter(currentTitle, currentContent.toString())); currentContent.setLength(0); } currentTitle = text; } else { currentContent.append(text).append("\n"); } } } if (currentContent.length() > 0) { chapters.add(new Chapter(currentTitle, currentContent.toString())); } return chapters; } // 解析PDF文档(需要PDFBox依赖) public List<Chapter> parsePdf(MultipartFile file) throws IOException { PDDocument document = PDDocument.load(file.getInputStream()); PDFTextStripper stripper = new PDFTextStripper(); String fullText = stripper.getText(document); return splitChapters(fullText); } private boolean isChapterTitle(String text) { return text.matches("^第[一二三四五六七八九]+章\\s+.+"); } } ``` 4. 文件上传控制器 ```java @RestController @RequestMapping("/api/files") public class FileController { @PostMapping("/upload") public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) { try { FileEntity fileEntity = new FileEntity(); fileEntity.setFileName(file.getOriginalFilename()); fileEntity.setFileType(FilenameUtils.getExtension(file.getOriginalFilename())); List<Chapter> chapters = fileParser.parse(file); fileEntity.setChapters(chapters); fileRepository.save(fileEntity); return ResponseEntity.ok(Map.of("message", "文件上传成功")); } catch (Exception e) { return ResponseEntity.status(500).body("文件处理失败"); } } @GetMapping("/search") public List<Chapter> searchChapters(@RequestParam String keyword) { return chapterRepository.findByContentContaining(keyword); } } ``` # 二、前端实现(Vue3 + Element Plus) 1. 文件上传组件 ```vue <template> <el-upload action="/api/files/upload" :before-upload="beforeUpload" :on-success="handleSuccess" > <el-button type="primary">点击上传文件(支持Word/PDF)</el-button> </el-upload> </template> <script setup> import { ElMessage } from 'element-plus'; const beforeUpload = (file) => { const isDocument = ['pdf', 'docx'].includes(file.name.split('.').pop().toLowerCase()); if (!isDocument) { ElMessage.error('仅支持PDF和Word文档'); return false; } return true; } const handleSuccess = (response) => { if (response.code === 200) { ElMessage.success('文件解析成功'); } } </script> ``` 2. 搜索组件 ```vue <template> <div class="search-container"> <el-input v-model="keyword" placeholder="输入章节关键词" @keyup.enter="search" /> <el-table :data="results"> <el-table-column prop="chapterTitle" label="章节标题"/> <el-table-column prop="content" label="内容摘要" :formatter="truncateContent"/> </el-table> </div> </template> <script setup> import { ref } from 'vue'; import axios from 'axios'; const keyword = ref(''); const results = ref([]); const search = async () => { try { const response = await axios.get('/api/files/search', { params: { keyword: keyword.value } }); results.value = response.data; } catch (error) { console.error('搜索失败', error); } } const truncateContent = (row) => { return row.content.length > 50 ? row.content.substr(0, 50) + '...' : row.content; } </script> ``` # 三、关键依赖配置(pom.xml) ```xml <!-- Word解析 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency> <!-- PDF解析 --> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>3.0.0</version> </dependency> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值