Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 15, 2026

Database Schema Analysis: Hibernate ORM vs SQL Scripts

Summary

This PR provides a comprehensive analysis of discrepancies between the Hibernate ORM data model and SQL-based PostgreSQL schema instantiation, along with fixes and recommendations.

Critical Fix: MatrixRow @lob Type Mismatch

The Error:

PSQLException: Bad value for type long : 0002000000000000000000000-0-0000000---00-000000

Root Cause: The @Lob annotation on MatrixRow.symbolString caused Hibernate to use OID-based CLOB handling, but the data is inserted via direct JDBC using setString(). When Hibernate tried to read back using getClob(), it interpreted text data as a CLOB OID.

Solution: Removed @Lob from MatrixRow.symbolString and PhyloTree.newickString, replaced with @Column(columnDefinition = "text").

Changes Made

  • Phase 1: Fix schema patches

    • Added missing patch 0011 to init_db_uptodate.pg
    • Made patches 0010 and 0011 idempotent
    • Added schema qualification and NULL handling
  • Phase 2: Fix LOB type mismatch

    • Removed @Lob from MatrixRow.symbolString
    • Removed @Lob from PhyloTree.newickString
    • Both now use @Column(columnDefinition = "text") for PostgreSQL TEXT type
  • Phase 3: Documentation

    • Updated /doc/technical-notes/HIBERNATE_VS_SQL_SCHEMA_ANALYSIS.md

Files Changed

  1. treebase-core/db/schema/init_db_uptodate.pg - Added patch 0011
  2. treebase-core/db/schema/patches/0010_fix-taxonlabel-linked-column-type.sql - Made idempotent
  3. treebase-core/db/schema/patches/0011_increase-citation-column-lengths.sql - Made idempotent
  4. treebase-core/src/main/java/org/cipres/treebase/domain/matrix/MatrixRow.java - Fixed @lob issue
  5. treebase-core/src/main/java/org/cipres/treebase/domain/tree/PhyloTree.java - Fixed @lob issue
  6. doc/technical-notes/HIBERNATE_VS_SQL_SCHEMA_ANALYSIS.md - Updated with findings

Test Results

Approach Tests Failures Errors
SQL-based 301 0 0
Hibernate create 301 12 9

Verified

  • All 301 tests pass with SQL-based schema
  • Code compiles without errors
  • CodeQL security check passes
Original prompt

I am gradually noticing that there are discrepancies between the data model as implemented in the hibernate ORM layer and what is being instantiated in PostgreSQL by the SQL scripts and patches.

I want to know the extent of this problem and fix it. My point of departure is that the ORM layer is the source of truth. As a first step, I want you to infer what the data model is in the ORM layer and what is created by the SQL scripts. The latter thing may be difficult to reconstruct because the SQL patches are applied incrementally. Perhaps it is an idea to apply them the way they are run when building the Docker container, and then export the schema from PostgreSQL in the end so that you have the final outcome that you can compare with Hibernate's model.

Once you have those two models, I want a detailed comparison of the two models: which implied tables are different, and how do they differ? I expect there two be differences both in structure (number of columns) and in column types.

Following that comparison, I then want to switch to a deployment workflow where we simply apply the Hibernate model to the underlying database. What I assume is that this will help us avoid having to keep patching the SQL-based instantiation because we just follow Hibernate as the source of truth so there can't be impedance mismatches between Java code (which becomes normative) and database.

However, I am unsure that this is indeed how it will pan out. What I want to know, therefore, is how the unit tests will perform if we do this. If we switch to a deployment workflow - both in the CI/CD (!) and in the Docker deployment - I need to know if any tests start failing and why.

Make a plan to address these concerns. Do a thorough, comprehensive, holistic analysis of:

  • the current database instantiation in CI/CD
  • the current database instantiation in Docker
  • differences in the implied schemata from Hibernate and direct SQL (by applying the patches sequentially and then exporting the result)
  • assessing the impact of Hibernate-based database instantiation

On the basis of this, I need a recommendation for how to proceed: keep patching the SQL or switch to Hibernate fully?


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI changed the title [WIP] Identify discrepancies between Hibernate ORM and PostgreSQL schema Fix Hibernate ORM vs SQL schema discrepancies and document analysis Jan 15, 2026
Copilot AI requested a review from rvosa January 15, 2026 08:31
@rvosa
Copy link
Contributor

rvosa commented Jan 15, 2026

@copilot, I need a comprehensive analysis of how character-state matrix data is entered into the database. What I noticed is that there are impedance mismatches when a nexus file is read by headless Mesquite and its contents are inserted into the database. I don't know if this is because there is a discrepancy between Hibernate ORM and the schema, as it is instantiated using the SQL patches, or something else. Based on your analysis in this PR, it is likely something else. The problem manifests specifically when character state matrix data is inserted.

Thinking back to the history of TreeBASE (this is 15 years ago) I recall that the initial database design was highly normalized, such that each individual cell in a character state matrix was a tuple. This was not scalable at all (e.g. a single matrix of 100 taxa with 1000 characters is 100k records, and that's still a small table by today's standards), so we worked on optimising this through denormalisation and possibly also by bypassing some of the ORM. The denormalisation consisted of inserting the entire matrix row as a single "symbol string". Whether we inserted this via Hibernate or a lower-level SQL operation, I don't remember.

I need you to perform an in-depth analysis of the workflow for inserting Nexus data into the database so we can identify the root cause of the stack trace below, which appears in the GUI when a Nexus file upload is performed. We have been stumped by this issue for a few days now, so this needs a thorough scan of all the code and code paths to figure this out. Clearly, it's not a direct discrepancy between Hibernate and PostgreSQL, so it's something else.

Data Access Failure
could not load an entity: [org.cipres.treebase.domain.matrix.MatrixRow#1]; SQL [select matrixrow0_.MATRIXROW_ID as matrixro1_45_1_, matrixrow0_.VERSION as version2_45_1_, matrixrow0_.MATRIX_ID as matrix_i4_45_1_, matrixrow0_.SymbolString as symbolst3_45_1_, matrixrow0_.TAXONLABEL_ID as taxonlab5_45_1_, segments1_.MATRIXROW_ID as matrixr20_54_3_, segments1_.ROWSEGMENT_ID as rowsegme1_54_3_, segments1_.ROWSEGMENT_ID as rowsegme1_54_0_, segments1_.VERSION as version2_54_0_, segments1_.EndIndex as endindex3_54_0_, segments1_.MATRIXROW_ID as matrixr20_54_0_, segments1_.CatalogNum as catalogn4_54_0_, segments1_.CollectionCode as collecti5_54_0_, segments1_.Collector as collecto6_54_0_, segments1_.Country as country7_54_0_, segments1_.Elevation as elevatio8_54_0_, segments1_.GenBAccession as genbacce9_54_0_, segments1_.InstAcronym as instacr10_54_0_, segments1_.Latitude as latitud11_54_0_, segments1_.Locality as localit12_54_0_, segments1_.Longitude as longitu13_54_0_, segments1_.Notes as notes14_54_0_, segments1_.OtherAccession as otherac15_54_0_, segments1_.SampleDate as sampled16_54_0_, segments1_.State as state17_54_0_, segments1_.StartIndex as startin18_54_0_, segments1_.TAXONLABEL_ID as taxonla21_54_0_, segments1_.Title as title19_54_0_ from MATRIXROW matrixrow0_ left outer join ROWSEGMENT segments1_ on matrixrow0_.MATRIXROW_ID=segments1_.MATRIXROW_ID where matrixrow0_.MATRIXROW_ID=?]; nested exception is org.hibernate.exception.DataException: could not load an entity: [org.cipres.treebase.domain.matrix.MatrixRow#1]

org.springframework.dao.DataIntegrityViolationException: could not load an entity: [org.cipres.treebase.domain.matrix.MatrixRow#1]; SQL [select matrixrow0_.MATRIXROW_ID as matrixro1_45_1_, matrixrow0_.VERSION as version2_45_1_, matrixrow0_.MATRIX_ID as matrix_i4_45_1_, matrixrow0_.SymbolString as symbolst3_45_1_, matrixrow0_.TAXONLABEL_ID as taxonlab5_45_1_, segments1_.MATRIXROW_ID as matrixr20_54_3_, segments1_.ROWSEGMENT_ID as rowsegme1_54_3_, segments1_.ROWSEGMENT_ID as rowsegme1_54_0_, segments1_.VERSION as version2_54_0_, segments1_.EndIndex as endindex3_54_0_, segments1_.MATRIXROW_ID as matrixr20_54_0_, segments1_.CatalogNum as catalogn4_54_0_, segments1_.CollectionCode as collecti5_54_0_, segments1_.Collector as collecto6_54_0_, segments1_.Country as country7_54_0_, segments1_.Elevation as elevatio8_54_0_, segments1_.GenBAccession as genbacce9_54_0_, segments1_.InstAcronym as instacr10_54_0_, segments1_.Latitude as latitud11_54_0_, segments1_.Locality as localit12_54_0_, segments1_.Longitude as longitu13_54_0_, segments1_.Notes as notes14_54_0_, segments1_.OtherAccession as otherac15_54_0_, segments1_.SampleDate as sampled16_54_0_, segments1_.State as state17_54_0_, segments1_.StartIndex as startin18_54_0_, segments1_.TAXONLABEL_ID as taxonla21_54_0_, segments1_.Title as title19_54_0_ from MATRIXROW matrixrow0_ left outer join ROWSEGMENT segments1_ on matrixrow0_.MATRIXROW_ID=segments1_.MATRIXROW_ID where matrixrow0_.MATRIXROW_ID=?]; nested exception is org.hibernate.exception.DataException: could not load an entity: [org.cipres.treebase.domain.matrix.MatrixRow#1] at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:216) at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:374) at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:334) at org.springframework.orm.hibernate5.HibernateTemplate.refresh(HibernateTemplate.java:542) at org.springframework.orm.hibernate5.HibernateTemplate.refresh(HibernateTemplate.java:537) at org.cipres.treebase.dao.AbstractDAO.refresh(AbstractDAO.java:41) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:234) at jdk.proxy3/jdk.proxy3.$Proxy115.refresh(Unknown Source) at org.cipres.treebase.service.study.SubmissionServiceImpl.addNexusFilesJDBC(SubmissionServiceImpl.java:637) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:241) at jdk.proxy3/jdk.proxy3.$Proxy141.addNexusFilesJDBC(Unknown Source) at org.cipres.treebase.web.controllers.UploadFileController.onSubmit(UploadFileController.java:219) at org.cipres.treebase.web.compat.CancellableFormController.processFormSubmission(CancellableFormController.java:209) at org.cipres.treebase.web.compat.CancellableFormController.handleRequestInternal(CancellableFormController.java:112) at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:177) at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:51) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) at javax.servlet.http.HttpServlet.service(HttpServlet.java:555) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:197) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:142) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:142) at net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:239) at net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:215) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:142) at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:176) at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145) at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92) at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:389) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:142) at org.displaytag.filter.ResponseOverrideFilter.doFilter(ResponseOverrideFilter.java:125) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:142) at org.springframework.orm.hibernate5.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:156) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:142) at com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119) at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:142) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:352) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:117) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:164) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:117) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:225) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:190) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:142) at net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:199) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:142) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:88) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:83) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:643) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:72) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:939) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1831) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:973) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:491) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) at java.base/java.lang.Thread.run(Thread.java:840) Caused by: org.hibernate.exception.DataException: could not load an entity: [org.cipres.treebase.domain.matrix.MatrixRow#1] at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:115) at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:37) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113) at org.hibernate.loader.Loader.loadEntity(Loader.java:2444) at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:77) at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:61) at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4550) at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4535) at org.hibernate.event.internal.DefaultRefreshEventListener.onRefresh(DefaultRefreshEventListener.java:213) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118) at org.hibernate.internal.SessionImpl.fireRefresh(SessionImpl.java:1314) at org.hibernate.internal.SessionImpl.refresh(SessionImpl.java:1277) at org.hibernate.engine.spi.CascadingActions$3.cascade(CascadingActions.java:142) at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:510) at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:434) at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220) at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:543) at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:474) at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:437) at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220) at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:153) at org.hibernate.event.internal.DefaultRefreshEventListener.onRefresh(DefaultRefreshEventListener.java:135) at org.hibernate.event.internal.DefaultRefreshEventListener.onRefresh(DefaultRefreshEventListener.java:52) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) at org.hibernate.internal.SessionImpl.fireRefresh(SessionImpl.java:1295) at org.hibernate.internal.SessionImpl.refresh(SessionImpl.java:1247) at org.springframework.orm.hibernate5.HibernateTemplate.lambda$refresh$6(HibernateTemplate.java:547) at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:367) ... 124 more Caused by: org.postgresql.util.PSQLException: Bad value for type long : 0002000000000000000000000-0-0000000---00-000000 at org.postgresql.jdbc.PgResultSet.toLong(PgResultSet.java:3397) at org.postgresql.jdbc.PgResultSet.getLong(PgResultSet.java:2635) at org.postgresql.jdbc.PgResultSet.getClob(PgResultSet.java:509) at org.postgresql.jdbc.PgResultSet.getClob(PgResultSet.java:494) at org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getClob(DelegatingResultSet.java:451) at org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getClob(DelegatingResultSet.java:451) at org.hibernate.type.descriptor.sql.ClobTypeDescriptor$1.doExtract(ClobTypeDescriptor.java:44) at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:47) at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:257) at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:253) at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:243) at org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:329) at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:3214) at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1887) at org.hibernate.loader.Loader.hydrateEntityState(Loader.java:1811) at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1784) at org.hibernate.loader.Loader.getRow(Loader.java:1624) at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:748) at org.hibernate.loader.Loader.getRowsFromResultSet(Loader.java:1047) at org.hibernate.loader.Loader.processResultSet(Loader.java:998) at org.hibernate.loader.Loader.doQuery(Loader.java:967) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:357) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:327) at org.hibernate.loader.Loader.loadEntity(Loader.java:2440) ... 148 more [« Back](http://localhost:8080/user/submissionList.html)

Copy link
Contributor Author

Copilot AI commented Jan 15, 2026

@copilot, I need a comprehensive analysis of how character-state matrix data is entered into the database. What I noticed is that there are impedance mismatches when a nexus file is read by headless M...

I've identified and fixed the root cause of the MatrixRow loading failure. The issue was a Hibernate @lob type mismatch:

Problem: The @Lob annotation on MatrixRow.symbolString caused Hibernate to use OID-based CLOB handling in PostgreSQL. However, the data is inserted via direct JDBC in DiscreteMatrixJDBC.batchUpdateRowSymbol() using setString(), which writes plain text. When Hibernate tried to read back using getClob(), it interpreted the text data as a CLOB OID, causing:

PSQLException: Bad value for type long : 0002000000000000000000000-0-0000000---00-000000

The Code Path:

  1. UploadFileController.onSubmit()SubmissionServiceImpl.addNexusFilesJDBC()
  2. NexusService.parseNexus() → Mesquite parses NEXUS file
  3. DiscreteMatrixJDBC.batchUpdateRowSymbol() → Inserts symbolstring via JDBC setString()
  4. getMatrixHome().refresh(m) → Hibernate tries to read back with CLOB semantics → FAILS

Solution (commit f9f4c13):

  • Removed @Lob from MatrixRow.symbolString
  • Removed @Lob from PhyloTree.newickString (preventive)
  • Both now use @Column(columnDefinition = "text") which ensures PostgreSQL TEXT type without CLOB semantics

This is a classic Hibernate/PostgreSQL impedance mismatch when mixing ORM and direct JDBC for large text fields.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • getahead.org
    • Triggering command: /opt/hostedtoolcache/CodeQL/2.23.8/x64/codeql/tools/linux64/java/bin/java /opt/hostedtoolcache/CodeQL/2.23.8/x64/codeql/tools/linux64/java/bin/java -jar /opt/hostedtoolcache/CodeQL/2.23.8/x64/codeql/xml/tools/xml-extractor.jar --fileList=/home/REDACTED/work/treebase/.codeql-scratch/dbs/java/working/files-to-index341488391652581566.list --sourceArchiveDir=/home/REDACTED/work/treebase/.codeql-scratch/dbs/java/src --outputDir=/home/REDACTED/work/treebase/.codeql-scratch/dbs/java/trap/java (dns block)
  • www.ubio.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED org.apache.maven.surefire.booter.ForkedBooter /home/REDACTED/work/treebase/treebase/treebase-core/target/surefire 2026-01-15T14-56-11_559-jvmRun1 surefire13612890221161791039tmp surefire_017578983536783117659tmp (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@rvosa rvosa marked this pull request as ready for review January 15, 2026 15:29
@rvosa rvosa merged commit aa2f710 into master Jan 15, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants