Skip to content
PYD ENTERPRISES

Migration guide

EJB entity beans to JPA and Spring Data: CMP fields, CMR relationships and EJB QL

EJB 2.x entity beans spread one entity across a bean class, two interfaces, ejb-jar.xml and a vendor mapping file. This guide shows how to assemble the full entity model, with its finders and lifecycle rules, for a JPA rebuild.

EJB entity beansJPA / Spring Data
DIFFICULTY
High
OUTPUT
OpenAPI / YAML
SYNTAX TRAPS
5 covered
WORKFLOW
6 steps

By Pranav Kulkarni, Head of Language Engineering · Updated 15 Oct 2026

Technical syntax challenges

Where EJB entity beans migrations go wrong.

01

Fields and columns are declared in different files

CMP 2.x fields are abstract getters in the bean class and <cmp-field> entries in ejb-jar.xml; the table and column names live in a vendor file such as weblogic-cmp-rdbms-jar.xml or WebSphere mapping metadata. An entity model needs all three joined.

02

CMR relationships and cascade delete

<ejb-relation> entries define multiplicity, navigability and <cascade-delete/>. They map to @OneToMany, @ManyToOne and cascade or orphan removal settings in JPA, and a missed cascade leaves orphaned line items after a delete.

03

EJB QL finders are business queries

SELECT OBJECT(o) FROM Order o WHERE o.status <> 'CLOSED' in a <query> element is a business definition of an open order. Each finder becomes a JPQL query or Spring Data method, and its predicates are rules the business should confirm.

04

Lifecycle methods hold defaults and rules

ejbCreate sets initial status and generated keys, ejbPostCreate adds relationships, and ejbStore sometimes recalculates derived fields before every write. Those become constructors, @PrePersist or @PreUpdate callbacks, or better, explicit domain methods.

05

BMP beans hide JDBC

Bean-managed persistence implements ejbLoad and ejbStore with handwritten JDBC, sometimes across several tables or with conditional updates. The SQL must be parsed to find the real mapping and any rules buried in it.

Extraction workflow

6 steps from source to signed-off spec.

The sequence a PYD ENTERPRISES Discovery Scan runs for this stack. Every output links back to source file and line range.

  1. 1

    Ingest bean classes, interfaces and descriptors

    Provide entity bean classes, local and remote interfaces, primary key classes, ejb-jar.xml and vendor CMP mapping descriptors.

  2. 2

    Join fields, columns and types

    PYD ENTERPRISES merges abstract accessors, cmp-field entries and vendor column mappings, typing each field from the accessor and the database column.

  3. 3

    Resolve relationships

    CMR definitions become typed associations with multiplicity, owning side, join columns and cascade behavior, checked against foreign keys in the DDL.

  4. 4

    Translate finders and select methods

    EJB QL in finders and ejbSelect methods is parsed and mapped to equivalent JPQL, with predicates extracted as named rules.

  5. 5

    Extract lifecycle and derived-field rules

    ejbCreate, ejbPostCreate, ejbStore and business methods are sliced for defaults, validations and derived values, emitted as rules with source links.

  6. 6

    Export the entity model and fixtures

    An entity model, a Mermaid ER diagram and fixtures that create, update and delete aggregates are exported for the JPA and Spring Data team.

Legacy in, specification out

The source, and what PYD ENTERPRISES extracts from it.

Left: the EJB entity beans construct as it sits in production. Right: the reviewed artifact your JPA / Spring Data engineers build against. PYD ENTERPRISES produces the spec and the tests; your team writes the service.

LEGACY SOURCE

ejb-jar.xml (OrderEJB)XML
1<entity>
2 <ejb-name>OrderEJB</ejb-name>
3 <local-home>com.example.order.OrderLocalHome</local-home>
4 <local>com.example.order.OrderLocal</local>
5 <ejb-class>com.example.order.OrderBean</ejb-class>
6 <persistence-type>Container</persistence-type>
7 <prim-key-class>java.lang.Long</prim-key-class>
8 <reentrant>false</reentrant>
9 <cmp-version>2.x</cmp-version>
10 <abstract-schema-name>Order</abstract-schema-name>
11 <cmp-field><field-name>orderId</field-name></cmp-field>
12 <cmp-field><field-name>status</field-name></cmp-field>
13 <cmp-field><field-name>total</field-name></cmp-field>
14 <primkey-field>orderId</primkey-field>
15 <query>
16 <query-method>
17 <method-name>findOpenByCustomer</method-name>
18 <method-params><method-param>java.lang.Long</method-param></method-params>
19 </query-method>
20 <ejb-ql>SELECT OBJECT(o) FROM Order o
21 WHERE o.customer.customerId = ?1 AND o.status &lt;&gt; 'CLOSED'</ejb-ql>
22 </query>
23</entity>

EXTRACTED ARTIFACT

Order.entity.yamlYAML
1entity: Order
2x-pyd-source: [ejb-jar.xml:112-141, OrderBean.java:20-64]
3table: ORDERS
4id: { field: orderId, type: Long, column: ORDER_ID, source: "sequence ORDER_SEQ in ejbCreate" }
5fields:
6 status: { type: String, column: STATUS, values: [OPEN, SHIPPED, CLOSED] }
7 total: { type: BigDecimal, column: TOTAL_AMT, precision: 12, scale: 2 }
8relationships:
9 customer: { kind: ManyToOne, target: Customer, joinColumn: CUST_ID, required: true }
10 lineItems: { kind: OneToMany, target: LineItem, mappedBy: order, cascadeDelete: true }
11queries:
12 findOpenByCustomer:
13 ejbQl: "o.customer.customerId = ?1 AND o.status <> 'CLOSED'"
14 jpql: "select o from Order o where o.customer.customerId = :customerId and o.status <> 'CLOSED'"
15 rule: ORD-Q-01 "open order = any status except CLOSED (includes SHIPPED)"
16rules:
17 - "ORD-INIT-01: ejbCreate sets status OPEN, total 0.00"
18 - "ORD-CALC-02: total = sum(qty * unitPrice) of lineItems, HALF_UP to 2 dp, in ejbStore"

Architectural best practices

Build the JPA / Spring Data side to the spec, not to the old shape.

  • Model aggregates explicitly (Order with LineItems) and put cascade rules on the aggregate root only.
  • Move derived-field calculations out of persistence callbacks into domain methods, so they run when the business event happens, not when the ORM flushes.
  • Name every finder predicate as a business rule; 'open' rarely means what a new developer assumes.
  • Keep generated key strategies identical during migration so existing identifiers and sequences stay valid.
  • Test with fixtures that create, modify and delete full aggregates and assert database state, including orphan rows.

Entity beans hold more than data

CMP entity beans were meant to be pure persistence, but long-lived systems used them as domain objects: totals recalculated in ejbStore, status transitions guarded in setters, defaults assigned in ejbCreate. A mechanical conversion to @Entity classes keeps those behaviors in callbacks, where they run at flush time in an order Hibernate decides, not the order the EJB container used. That is how a migration that compiles and passes unit tests ends up with totals that lag one change behind.

PYD ENTERPRISES extracts the entity model and the behaviors separately, with links to the bean class and descriptor lines. Your engineers write the JPA entities and Spring Data repositories against the reviewed model. PYD ENTERPRISES does not generate them.

Reserved words and quoting

The abstract schema name Order is fine in EJB QL and JPQL, but ORDER is a reserved word in SQL. Legacy vendor mappings often map it to ORDERS or a quoted identifier. The entity model records the real table name from the vendor descriptor, and the review queue flags entity names that collide with SQL reserved words so the JPA mapping sets an explicit @Table name rather than relying on defaults.

FAQ

EJB entity beans to JPA / Spring Data questions

Does PYD ENTERPRISES support EJB 1.1 CMP as well as 2.x?

Yes. EJB 1.1 CMP uses public fields and different descriptor elements; both models are parsed and produce the same entity model output.

How are BMP entity beans handled?

The JDBC code in ejbLoad, ejbStore, ejbCreate and ejbRemove is parsed, SQL statements are analyzed for tables and columns, and the mapping is reconstructed from them, with conditional logic extracted as rules.

Can the output be used with Hibernate directly?

The entity model is framework neutral. Teams use it to write JPA entities that run on Hibernate or another provider, and the fixtures verify persisted state regardless of provider.

Get the spec for your EJB entity beans estate.

Request a codebase scan and review extracted rules, contracts and fixtures for your own EJB entity beans estate, inside your VPC.