The content of element type "class" must match "(meta*,subselect?,cache?,synchro

本文通过一个具体的案例,详细解析了Hibernate中XML映射文件配置错误导致的问题及解决过程。作者分享了如何定位并修正XML文件中关于class元素配置的错误。

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

The content of element type "class" must match "(meta*,subselect?,cache?,synchronize*,comment?,tuplizer*,(id|composite-id),discriminator?,natural-id?,(version|timestamp)?,(property|many-to-one|one-to-one|component|dynamic-component|properties|any|map|set|list|bag|idbag|array|primitive-array)*,((join*,subclass*)|joined-subclass*|union-subclass*),loader?,sql-insert?,sql-update?,sql-delete?,filter*,fetch-profile*,resultset*,(query|sql-query)*)".

 

Caused by: org.hibernate.InvalidMappingException: Unable to read XML
	at org.hibernate.internal.util.xml.MappingReader.readMappingDocument(MappingReader.java:109)
	at org.hibernate.cfg.Configuration.add(Configuration.java:488)
	at org.hibernate.cfg.Configuration.add(Configuration.java:484)
	at org.hibernate.cfg.Configuration.add(Configuration.java:657)
	at org.hibernate.cfg.Configuration.addInputStream(Configuration.java:695)
	at org.springframework.orm.hibernate4.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:291)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1571)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1509)
	... 181 more
Caused by: org.xml.sax.SAXParseException: The content of element type "class" must match "(meta*,subselect?,cache?,synchronize*,comment?,tuplizer*,(id|composite-id),discriminator?,natural-id?,(version|timestamp)?,(property|many-to-one|one-to-one|component|dynamic-component|properties|any|map|set|list|bag|idbag|array|primitive-array)*,((join*,subclass*)|joined-subclass*|union-subclass*),loader?,sql-insert?,sql-update?,sql-delete?,filter*,fetch-profile*,resultset*,(query|sql-query)*)".
	at org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
	at org.apache.xerces.util.ErrorHandlerWrapper.error(Unknown Source)
	at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
	at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
	at org.apache.xerces.impl.dtd.XMLDTDValidator.handleEndElement(Unknown Source)
	at org.apache.xerces.impl.dtd.XMLDTDValidator.endElement(Unknown Source)
	at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
	at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
	at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
	at org.dom4j.io.SAXReader.read(SAXReader.java:465)
	at org.hibernate.internal.util.xml.MappingReader.readMappingDocument(MappingReader.java:78)
	... 188 more

 

解决这个问题弄得我想死的心都有了,看上去还挺简单,这不XML有问题嘛,都写着Unable to read XML

所以先检查了Server 端所有的xml配置文件,什么hibernate啊,什么eventMapping啊,还有business,dao的xml都看了,没看出什么毛病

 

The content of element type "class" must match ……

从这个error log呢明显觉得是class元素的配置有问题,我以为是以前一向可能出现的问题,property写的时候写错啦,或者双引号之类的弄丢啦,检查了一大堆,最后借助百度对比其他的hibernate文件,方知自己sa缺了,把versionNo property位置放错了。手贱,没事换什么位置 ,放后面又不会好看点。。。。当时写的时候就觉得这属性应该给creat_user,create_date,update_user,update_date站在一起。结果呢,结果sa缺的找了一上午,也没解决问题,好在这会终于清醒了,oh,阿门……

 

<hibernate-mapping>
  <class dynamic-update="true" lazy="true" table="TMP_DK_ASSESSMENT_UPLOAD" dynamic-insert="true" name="com.shkco.adsr3.staticdata.vo.TmpDkAssessmentInfo">
    <id name="id" type="long" column="ID">
      <generator class="sequence">
        <param name="sequence">SEQ_DK_ASSESSMENT_UPLOAD_ID</param>
      </generator>
    </id>
    <version unsaved-value="undefined" name="versionNo" column="VERSION_NO" type="long"/>
    <property name="ccdRefId" column="CCD_REF_ID" not-null="true" type="long"/>
    <property name="dkReviewDate" column="DK_REVIEW_DATE" not-null="true" type="timestamp"/>
    <property name="createDate" column="CREATE_DATE" not-null="true" type="timestamp"/>
    <property name="createUser" column="CREATE_USER" not-null="true" type="long"/>
    <property name="updateDate" column="UPDATE_DATE" not-null="true" type="timestamp"/>
    <property name="updateUser" column="UPDATE_USER" not-null="true" type="long"/>
  </class>
</hibernate-mapping>

 

class里面的元素,id标签过了一定要是version,再是property,多之前就是因为把version放到了createDate前面,而自己全然不知,无知。。。。

记录长个记醒……

 

copy也需要按套路来,不然死很惨都不知道……

<think>好的,用户之前遇到了一个JSQLParser的类型转换错误,现在他们想了解在Spring Boot中如何递归处理查询结构或使用访问者模式来解决这个问题。我需要详细解释这两种方法的具体实现步骤,并且结合Spring Boot的环境。 首先,用户可能已经知道错误的根源,但需要具体的代码示例和如何在Spring Boot项目中应用。他们可能不太熟悉JSQLParser的高级用法,或者如何在框架中整合这些处理逻辑。 我应该先回顾一下之前的解决方案,确保用户理解递归处理和访问者模式的基本概念。然后,分步骤讲解如何在Spring Boot中实现这两种方法,可能需要提供代码示例,并解释每个部分的作用。 对于递归处理,需要展示如何解析SQL语句,检查是否是SetOperationList,然后递归处理每个子查询。要考虑到可能有多个UNION ALL的情况,所以循环处理每个子查询,并确保每个子查询都被正确处理,比如修改列名或条件。 访问者模式部分,需要创建一个访问者类,继承SelectVisitorAdapter,并重写visit方法,分别处理PlainSelect和SetOperationList。在访问者中,当遇到SetOperationList时,继续遍历其子查询,并应用相同的访问者,这样可以递归处理所有层级的查询。 还需要提醒用户注意依赖版本,确保JSQLParser的版本兼容性,比如4.5以上。此外,要给出在Spring Boot中的实际应用场景,比如在服务层解析和修改SQL,可能是在AOP中拦截查询进行动态处理,或者在自定义的Repository中处理SQL。 用户可能需要具体的代码示例,所以在思考过程中,要准备详细的代码块,并注释关键部分。同时,考虑可能的异常处理和调试方法,比如打印AST结构,帮助用户理解解析后的SQL结构。 最后,总结两种方法的适用场景,帮助用户根据具体需求选择递归处理还是访问者模式。访问者模式更优雅,适合复杂结构,而递归处理更直观,适合简单情况。还要提醒用户在实际应用中注意性能问题,特别是在处理复杂SQL时,递归可能导致栈溢出,需要确保有适当的终止条件。</think>在Spring Boot项目中处理此类SQL解析问题时,可以通过以下两种方式实现递归处理或访问者模式。这里以 **JSQLParser** 为例,结合具体代码示例说明: --- ### 一、递归处理查询结构 #### 适用场景 - 需要逐层解析UNION ALL嵌套结构 - 对查询树进行深度优先遍历 - 简单查询修改场景 #### 实现步骤 1. **添加依赖** ```xml <!-- pom.xml --> <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>4.9</version> <!-- 推荐4.5+版本 --> </dependency> ``` 2. **核心处理逻辑** ```java @Service public class SqlParserService { public void processUnionQuery(String sql) throws JSQLParserException { Statement statement = CCJSqlParserUtil.parse(sql); handleStatement(statement); // 入口方法 } private void handleStatement(Statement statement) { if (statement instanceof Select) { Select select = (Select) statement; handleSelectBody(select.getSelectBody()); } } private void handleSelectBody(SelectBody selectBody) { if (selectBody instanceof PlainSelect) { handlePlainSelect((PlainSelect) selectBody); } else if (selectBody instanceof SetOperationList) { SetOperationList setOp = (SetOperationList) selectBody; for (SelectBody subSelect : setOp.getSelects()) { handleSelectBody(subSelect); // 递归处理子查询 } } } private void handlePlainSelect(PlainSelect plainSelect) { // 具体处理单个SELECT语句 System.out.println("处理基础查询: " + plainSelect); // 例如修改WHERE条件: // plainSelect.setWhere(new EqualsTo(new Column("status"), new StringValue("ACTIVE"))); } } ``` --- ### 二、访问者模式实现 #### 适用场景 - 需要处理复杂嵌套结构 - 需要扩展多种操作(如条件注入、列修改) - 遵循开闭原则的代码设计 #### 实现步骤 1. **自定义访问者** ```java public class CustomSqlVisitor extends SelectVisitorAdapter { @Override public void visit(PlainSelect plainSelect) { // 处理普通SELECT injectCondition(plainSelect); // 示例:注入条件 } @Override public void visit(SetOperationList setOp) { // 处理UNION ALL for (SelectBody select : setOp.getSelects()) { select.accept(this); // 继续访问子查询 } } private void injectCondition(PlainSelect plainSelect) { Expression where = plainSelect.getWhere(); // 添加 status = 'ACTIVE' Expression newCondition = new EqualsTo( new Column("status"), new StringValue("ACTIVE") ); if (where != null) { newCondition = new AndExpression(where, newCondition); } plainSelect.setWhere(newCondition); } } ``` 2. **在Service中调用** ```java @Service public class SqlVisitorService { public String modifySqlWithVisitor(String originalSql) throws JSQLParserException { Statement statement = CCJSqlParserUtil.parse(originalSql); statement.accept(new CustomSqlVisitor()); // 应用访问者 return statement.toString(); // 返回修改后的SQL } } ``` --- ### 三、Spring Boot整合示例 #### 控制器层调用 ```java @RestController @RequestMapping("/sql") public class SqlController { @Autowired private SqlParserService sqlParserService; @Autowired private SqlVisitorService sqlVisitorService; @PostMapping("/process") public String processSql(@RequestBody String sql) { try { // 方式1:递归处理 sqlParserService.processUnionQuery(sql); // 方式2:访问者模式 String modifiedSql = sqlVisitorService.modifySqlWithVisitor(sql); return "处理后的SQL: " + modifiedSql; } catch (JSQLParserException e) { return "SQL解析错误: " + e.getMessage(); } } } ``` --- ### 四、关键实践建议 1. **AST调试技巧** ```java // 打印解析树结构 System.out.println(CCJSqlParserUtil.parse(sql).toString()); ``` 2. **版本兼容性处理** ```java // 检查获取的SelectBody类型 if (selectBody.getClass().getName().contains("SetOperationList")) { // 处理UNION操作 } ``` 3. **动态SQL生成** ```java // 使用JSqlParser工具类重建SQL String modifiedSql = statement.toString(); ``` 4. **性能优化** ```java // 使用缓存存储已解析的SQL模板 @Cacheable(value = "parsedSql", key = "#sql") public Statement getParsedStatement(String sql) throws JSQLParserException { return CCJSqlParserUtil.parse(sql); } ``` --- ### 五、典型应用场景 1. **多租户数据隔离** ```java // 在所有查询中自动添加 tenant_id = ? visitor.visit(plainSelect -> { plainSelect.addWhere(new EqualsTo(new Column("tenant_id"), param)); }); ``` 2. **动态字段脱敏** ```java // 将SELECT phone改为SELECT mask(phone) plainSelect.getSelectItems().replaceAll(item -> { if (item.toString().contains("phone")) { return new SelectExpressionItem(new Function("mask", item.getExpression())); } return item; }); ``` 3. **查询性能分析** ```java // 统计所有涉及的表 Set<String> tables = new HashSet<>(); visitor.visit(plainSelect -> { plainSelect.getFromItem().accept(new FromVisitor() { @Override public void visit(Table table) { tables.add(table.getName()); } }); }); ``` --- 选择方案建议: - **简单结构**:使用递归处理(代码直观) - **复杂操作**:访问者模式(扩展性强) - **高频调用**:结合缓存机制(提升性能) 如果需要处理其他SQL类型(如INSERT/UPDATE),可通过实现`StatementVisitor`进行扩展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值