VMS Tech Demo 3 - from index-sequential file to SQLite database

Content:

  1. Introduction
  2. Setup
  3. Mapping
  4. Simple example
  5. Relation example (array of subrecords in record)
  6. Relation example (two files - records and subrecords)
  7. Inheritance/variant example
  8. Complex queries
  9. Locking
  10. Conclusion

Introduction:

As part of application modernization for VMS applications then one thing to consider is migrating from RMS index-sequential files to a relational database.

It is not a given that the migration should happen. RMS index-sequential files are really a NoSQL Key Value Store database and such databases are widely used today.

But relational database has become the default choice. In 1991 it may have been: chose RMS index-sequential file unless there are requirements that mandates a relational database. In 2021 is is: chose a relational database unless there are requirements that mandates a NoSQL Key Value Store.

The benefits of using a relational database are usually big:

For more details on benefits and non-benefits see VMS: Index-sequential file vs relational database.

For most usages of RMS index-sequential files then SQLite will be the most obvious choice for relational database. It is an embedded database so no database server to manage.

But if access is highly concurrent or there is a desire to make the database accessble over the network, then it may make more sense to go with a database server (Rdb, MySQL-MariaDB, Mimer etc.). See Access VMS database - native databases and Access VMS database - JVM databases for more info on what is available.

Setup:

The examples will use SQLite as database.

The conversion can obviously be written in any language that can access both RMS index-sequential files and SQLite.

But the approach chosen is a declarative mapping approach. Instead of reading a record, parsing the record and executing a SQL INSERT statement then records get mapped to a class, another class get mapped to the tables and conversion between the two classes get auto mapped based on property names. The actual conversion code becomes trivial.

The language combo to make that happen is Java and Python with the Python code running in the JVM via Jython.

For mapping to the RMS index-sequential files my ISAM library is used.

For mapping to SQLite standard JPA with Hibernate as implementation is used. For more info about JPA see Java Database Access.

This article will show 4 examples:

For each example will the following be shown:

Cobol and Pascal before code will use built-in index-sequential file support. For intro see VMS index-sequential files.

C after code will use standard SQLite C library. For another example see here.

Pascal after code will use my PSQLite library. For another example see here.

Python and PHP after code will use standard SQLite drivers. For more examples see here and here.

Java affter code will use standard SQLite JDBC driver (which is type 2). For another example see here.

For JPA Hibernate 3 will be used.

Most readers will not find everything relevant, but most readers should find something relevant. If anyone is reading all the code then there will be a lot of repetitive stuff, but the article is written to make it readable for those only reading one or two examples in one or two languages.

Mapping:

Mapping between record in insex-sequential and Java class:

The mapping is done using annotations on a data class.

The main annotations are:

@Struct
is put on classes that are to be mapped to records
@Structfield(n=x, type=FieldType.XXXX)
is put on fields in the record - n is the field number, type is the field type (there are more attributes)
@Key(n=x)
is put on fields that are keys - n is the key number

So if we have a record with one 32 bit integer and one fixed length text field with 10 bytes then it can be mapped to:

import dk.vajhoej.isam.KeyField;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct
public class Something {
    @KeyField(n=0)
    @StructField(n=0, type=FieldType.INT4)
    public int id;
    @StructField(n=1, type=FieldType.FIXSTR, length=10)
    public String strval;
}

And when using Java style with private fields and public getters/setters:

import dk.vajhoej.isam.KeyField;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct
public class Something {
    @KeyField(n=0)
    @StructField(n=0, type=FieldType.INT4)
    private int id;
    @StructField(n=1, type=FieldType.FIXSTR, length=10)
    private String strval;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getSval() {
        return sval;
    }
    public void setSval(String sval) {
        this.sval = sval;
    }
}

Reading records in Jython is easy:

from dk.vajhoej.isam import Key0
from dk.vajhoej.isam.local import LocalIsamSource
import Something

db = LocalIsamSource(filename, 'dk.vajhoej.vms.rms.IndexSequential', False)
it = db.readGE(AddressISAM, Key0(0))
while it.read():
    rec = it.current()
    ...
it.close()
db.close()

Mapping between Java class and relational database table using JPA:

The mapping is done using annotations on a data class.

The main annotations are:

@Entity
is put on data classes that are persisted to database
@Table(name="xxxx")
is put on classes to tell name of database table
@Column(name="xxxx")
is put on fields to tell name of table column
@Id
Is put on field that us primary key in table

So the class:

public class Something {
    private int id;
    private String strval;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getSval() {
        return sval;
    }
    public void setSval(String sval) {
        this.sval = sval;
    }
}

will look like:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="something")
public class Something {
    private int id;
    private String strval;
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="sval")
    public String getSval() {
        return sval;
    }
    public void setSval(String sval) {
        this.sval = sval;
    }
}

Persisting object in Jython is easy:

from javax.persistence import Persistence
import Something

emf = Persistence.createEntityManagerFactory(database)
em = emf.createEntityManager()
...
rec = Something();
...
em.persist(rec)
...
em.close()
emf.close()

The database (name, database driver, database URL, database credential etc.) get defined in META-INF/persistence.xml.

Auto mapping between classes:

The auto mapping is trivial using Java reflection - iterate all properties in FROM class and TO class and create list of pairs of getters and setters for properties with same name.

The calling code in Jython looks like:

import X
import Y
import AutoMap

mapper = AutoMap(X, Y)
xrec = X()
yrec = Y()
mapper.convert(xrec, yrec)

Simple example:

Data model:

Before records:

id name address
1 A A A Road 11
2 B B B Road 22

After database tables:

address

id name address
1 A A A Road 11
2 B B B Road 22

Before:

Insert:

IDENTIFICATION DIVISION.
PROGRAM-ID.INSERT-BEFORE-1.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT OPTIONAL ADDRESS-FILE ASSIGN TO "address.isq" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS ADDRESS-ID.

DATA DIVISION.
FILE SECTION.
FD ADDRESS-FILE.
01 ADDRESS-RECORD.
    03 ADDRESS-ID PIC 9(8) COMP.
    03 ADDRESS-NAME PIC X(32).
    03 ADDRESS-ADDRESS PIC X(64).
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
    OPEN I-O ADDRESS-FILE
    MOVE 1 TO ADDRESS-ID
    MOVE "A A" TO ADDRESS-NAME
    MOVE "A Road 11" TO ADDRESS-ADDRESS
    PERFORM INSERT-PARAGRAPH
    MOVE 2 TO ADDRESS-ID
    MOVE "B B" TO ADDRESS-NAME
    MOVE "B Road 22" TO ADDRESS-ADDRESS
    PERFORM INSERT-PARAGRAPH
    CLOSE ADDRESS-FILE
    STOP RUN.
INSERT-PARAGRAPH.
    WRITE ADDRESS-RECORD
        INVALID KEY DISPLAY "Error writing"
        NOT INVALID KEY CONTINUE
    END-WRITE.

Build and run:

$ cob insert_before_1
$ link insert_before_1
$ run insert_before_1

address_isam.pas:

type
   address = record
                id : [key(0)]unsigned;
                name : packed array [1..32] of char;
                address : packed array [1..64] of char;
             end;

program insert_before_2(input, output);

%include 'address_isam.pas'

var
   addr : address;
   db : file of address;

begin
   open(db, 'address.isq', unknown, organization := indexed, access_method := keyed);
   addr.id := 3;
   addr.name := 'C C';
   addr.address := 'C Road 33';
   db^ := addr;
   put(db);
   addr.id := 4;
   addr.name := 'D D';
   addr.address := 'D Road 44';
   db^ := addr;
   put(db);
   close(db);
end.

Build and run:

$ pas insert_before_2
$ link insert_before_2
$ run insert_before_2

List:

IDENTIFICATION DIVISION.
PROGRAM-ID.LIST-BEFORE-1.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT OPTIONAL ADDRESS-FILE ASSIGN TO "address.isq" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS ADDRESS-ID.

DATA DIVISION.
FILE SECTION.
FD ADDRESS-FILE.
01 ADDRESS-RECORD.
    03 ADDRESS-ID PIC 9(8) COMP.
    03 ADDRESS-NAME PIC X(32).
    03 ADDRESS-ADDRESS PIC X(64).
WORKING-STORAGE SECTION.
01 EOF-FLAG PIC X.         
01 ID PIC 9(8) DISPLAY.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
    OPEN I-O ADDRESS-FILE
    MOVE 'N' TO EOF-FLAG
    PERFORM UNTIL EOF-FLAG = 'Y'
        READ ADDRESS-FILE NEXT
            AT END MOVE 'Y' TO EOF-FLAG
            NOT AT END PERFORM SHOW-PARAGRAPH
        END-READ
    END-PERFORM
    CLOSE ADDRESS-FILE
    STOP RUN.
SHOW-PARAGRAPH.
    MOVE ADDRESS-ID TO ID
    DISPLAY ID "|" ADDRESS-NAME "|" ADDRESS-ADDRESS.

Build and run:

$ cob list_before_1
$ link list_before_1
$ run list_before_1

address_isam.pas:

type
   address = record
                id : [key(0)]unsigned;
                name : packed array [1..32] of char;
                address : packed array [1..64] of char;
             end;

program list_before_2(input, output);

%include 'address_isam.pas'

var
   addr : address;
   db : file of address;

begin
   open(db, 'address.isq', unknown, organization := indexed, access_method := keyed);
   resetk(db, 0);
   while not eof(db) do begin
      addr := db^;
      writeln(addr.id,'|',addr.name,'|',addr.address);
      get(db);
   end;
   close(db);
end.

Build and run:

$ pas list_before_2
$ link list_before_2
$ run list_before_2

Convert:

import dk.vajhoej.isam.KeyField;
import dk.vajhoej.record.Alignment;
import dk.vajhoej.record.Endian;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct(endianess=Endian.LITTLE, alignment=Alignment.ALIGN1)
public class AddressISAM {
    @KeyField(n=0)
    @StructField(n=0, type=FieldType.INT4)
    private int id;
    @StructField(n=1, type=FieldType.FIXSTR, length=32)
    private String name;
    @StructField(n=2, type=FieldType.FIXSTR, length=64)
    private String address;
    public AddressISAM() {
        this(0, "", "");
    }
    public AddressISAM(int id, String name, String addreess) {
        this.id = id;
        this.name = name;
        this.address = address;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="address")
public class AddressJPA {
    private int id;
    private String name;
    private String address;
    public AddressJPA() {
        this(0, "", "");
    }
    public AddressJPA(int id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="name")
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Column(name="address")
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class AutoMap<TFROM,TTO> {
    public static class MethodPair {
        private Method getter;
        private Method setter;
        private boolean trim;
        public MethodPair(Method getter, Method setter, boolean trim) {
            this.getter = getter;
            this.setter = setter;
            this.trim = trim;
        }
        public Method getGetter() {
            return getter;
        }
        public Method getSetter() {
            return setter;
        }
        public boolean getTrim() {
            return trim;
        }
    }
    private List<MethodPair> conv;
    public AutoMap(Class<TFROM> from, Class<TTO> to) throws IntrospectionException {
        this(from, to, false);
    }
    public AutoMap(Class<TFROM> from, Class<TTO> to, boolean trim) throws IntrospectionException {
        conv = new ArrayList<MethodPair>();
        for(PropertyDescriptor pdfrom : Introspector.getBeanInfo(from).getPropertyDescriptors()) {
            for(PropertyDescriptor pdto : Introspector.getBeanInfo(to).getPropertyDescriptors()) {
                if(pdfrom.getName().equals(pdto.getName())) {
                    Method getter = pdfrom.getReadMethod();
                    Method setter = pdto.getWriteMethod();
                    if(getter != null && setter != null) {
                        conv.add(new MethodPair(getter, setter, trim && pdfrom.getPropertyType().equals(String.class)));
                    }
                }
            }
        }
    }
    public void convert(TFROM from, TTO to) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        for(MethodPair mp : conv) {
            if(mp.getTrim()) {
                mp.getSetter().invoke(to, ((String)mp.getGetter().invoke(from)).trim());
            } else {
                mp.getSetter().invoke(to, mp.getGetter().invoke(from));
            }
        }
    }
}
# stuff for JPA
from java.util.logging import Level
from java.util.logging import Logger
from javax.persistence import Persistence

# stuff for ISAM
from dk.vajhoej.isam import Key0
from dk.vajhoej.isam.local import LocalIsamSource

#input class
import AddressISAM
#output class
import AddressJPA
# auto map
import AutoMap

# open JPA
Logger.getLogger("org.hibernate").setLevel(Level.OFF)
emf = Persistence.createEntityManagerFactory("Address")
em = emf.createEntityManager()

# open ISAM
db = LocalIsamSource('address.isq', 'dk.vajhoej.vms.rms.IndexSequential', False)

# process all records
mapper = AutoMap(AddressISAM, AddressJPA, True)
it = db.readGE(AddressISAM, Key0(0))
em.getTransaction().begin()
while it.read():
    oldrec = it.current()
    newrec = AddressJPA()
    mapper.convert(oldrec, newrec)
    em.persist(newrec)
it.close()
em.getTransaction().commit()

# close ISAM
db.close()

# close JPA
em.close()
emf.close()

META-INF/persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="Address">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <class>AddressJPA</class>
      <exclude-unlisted-classes/>
      <properties>
          <!--<property name="show_sql" value="true"/>-->
          <property name="hibernate.connection.driver_class" value="org.sqlite.JDBC"/>
          <property name="hibernate.connection.url" value="jdbc:sqlite:address.db"/>
          <property name="hibernate.connection.username" value=""/>
          <property name="hibernate.connection.password" value=""/>
          <property name="hibernate.dialect" value="org.hibernate.dialect.SQLiteDialect"/>
      </properties>
   </persistence-unit>
</persistence>

Database structure:

CREATE TABLE address (
    id INTEGER NOT NULL,
    name VARCHAR(32),
    address VARCHAR(64),
    PRIMARY KEY(id)
);

After:

Insert:

address.h:

struct address
{
    int id;
    char name[33];
    char address[65];
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "sqlite3.h"

#include "address.h"

int main()
{
    sqlite3 *con;
    sqlite3_stmt *ins;
    char *sqlstr = "INSERT INTO address VALUES(?,?,?)";
    int stat;
    struct address addr;
    stat = sqlite3_open("address.db", &con);
    stat = sqlite3_prepare(con, sqlstr, strlen(sqlstr), &ins, NULL);
    addr.id = 5;
    strcpy(addr.name, "E E");
    strcpy(addr.address, "E Road 55");
    sqlite3_bind_int(ins, 1, addr.id);
    sqlite3_bind_text(ins, 2, addr.name, strlen(addr.name), NULL);
    sqlite3_bind_text(ins, 3, addr.address, strlen(addr.address), NULL);
    stat = sqlite3_step(ins);
    sqlite3_finalize(ins);
    stat = sqlite3_prepare(con, sqlstr, strlen(sqlstr), &ins, NULL);
    addr.id = 6;
    strcpy(addr.name, "F F");
    strcpy(addr.address, "F Road 66");
    sqlite3_bind_int(ins, 1, addr.id);
    sqlite3_bind_text(ins, 2, addr.name, strlen(addr.name), NULL);
    sqlite3_bind_text(ins, 3, addr.address, strlen(addr.address), NULL);
    stat = sqlite3_step(ins);
    sqlite3_finalize(ins);
    sqlite3_close(con);
    return 0;
}

Build and run:

$ cc/include=sqlite3_include: insert_after_1
$ link insert_after_1 + sys$input/opt
sqlite3shr/share
$
$ run insert_after_1

address.pas:

type
   address = record
                id : unsigned;
                name : varying[32] of char;
                address : varying[64] of char;
             end;

[inherit('psqlitedir:sqlite', 'psqlitedir:psqlite')]
program insert_after_2(input, output);

%include 'address.pas'

var
   con : sqlite_ptr;
   ins : sqlite_stmt_ptr;
   addr : address;

begin
   con := psqlite_open('address.db');
   ins := psqlite_prepare(con, 'INSERT INTO address VALUES(?,?,?)');
   addr.id := 7;
   addr.name := 'G G';
   addr.address := 'G Road 77';
   psqlite_bind_int(ins, 1, addr.id);
   psqlite_bind_text(ins, 2, addr.name);
   psqlite_bind_text(ins, 3, addr.address);
   psqlite_step_nonquery(ins);
   psqlite_finalize(ins);
   ins := psqlite_prepare(con, 'INSERT INTO address VALUES(?,?,?)');
   addr.id := 8;
   addr.name := 'H H';
   addr.address := 'H Road 88';
   psqlite_bind_int(ins, 1, addr.id);
   psqlite_bind_text(ins, 2, addr.name);
   psqlite_bind_text(ins, 3, addr.address);
   psqlite_step_nonquery(ins);
   psqlite_finalize(ins);
   psqlite_close(con);
end.

Build and run:

$ pas insert_after_2
$ link insert_after_2 + sys$input/opt + psqlitedir:sqlite/opt
psqlitedir:psqlite
psqlitedir:sqlite
$
$ run insert_after_2

address.py:

class Address(object):
    def __init__(self, _id = 0, _name = '', _address = ''):
        self.id = _id
        self.name = _name
        self.address = _address

import sqlite3

from address import Address

con = sqlite3.connect('address.db')
c = con.cursor()
addr = Address(9, 'I I', 'I Road 99')
c.execute('INSERT INTO address VALUES(?,?,?)', (addr.id, addr.name, addr.address))
addr = Address(10, 'J J', 'J Road 1010')
c.execute('INSERT INTO address VALUES(?,?,?)', (addr.id, addr.name, addr.address))
con.commit()
con.close()

address.php:

<?php
class Address {
    public $id;
    public $name;
    public $address;
    public function __construct($id = 0, $name = '', $address = '') {
        $this->id = $id;
        $this->name = $name;
        $this->address = $address;
    }
}
?>
<?php
include '/disk2/arne/migr/simple/address.php';

$con = new PDO('sqlite:/disk2/arne/migr/simple/address.db');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$stmt = $con->prepare('INSERT INTO address VALUES(:ID,:NAME,:ADDRESS)');
$addr = new Address(11, 'K K', 'K Road 1111');
$stmt->execute(array(':ID' => $addr->id, ':NAME' => $addr->name, ':ADDRESS' => $addr->address));
$addr = new Address(12, 'L L', 'L Road 1212');
$stmt->execute(array(':ID' => $addr->id, ':NAME' => $addr->name, ':ADDRESS' => $addr->address));
?>
public class Address {
    private int id;
    private String name;
    private String address;
    public Address() {
        this(0, "", "");
    }
    public Address(int id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;

public class InsertAfter5 {
    public static void main(String[] args) throws Exception {
        Class.forName("org.sqlite.JDBC");
        Connection con = DriverManager.getConnection("jdbc:sqlite:address.db");
        PreparedStatement pstmt = con.prepareStatement("INSERT INTO address VALUES(?,?,?)");
        Address addr;
        addr = new Address(13, "M M", "M Road 1313");
        pstmt.setInt(1, addr.getId());
        pstmt.setString(2, addr.getName());
        pstmt.setString(3, addr.getAddress());
        pstmt.executeUpdate();
        addr = new Address(14, "N N", "N Road 1414");
        pstmt.setInt(1, addr.getId());
        pstmt.setString(2, addr.getName());
        pstmt.setString(3, addr.getAddress());
        pstmt.executeUpdate();
        pstmt.close();
        con.close();
    }
}

Build and run:

$ javac InsertAfter5.java Address.java
$ java -cp .:/javalib/sqlite-jdbc-3_14_1-vms.jar "InsertAfter5"
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="address")
public class AddressJPA {
    private int id;
    private String name;
    private String address;
    public AddressJPA() {
        this(0, "", "");
    }
    public AddressJPA(int id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="name")
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Column(name="address")
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;

public class InsertAfter6 {
    public static void main(String[] args) throws Exception {
        Logger.getLogger("org.hibernate").setLevel(Level.OFF);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Address");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        AddressJPA addr;
        addr = new AddressJPA(15, "O O", "O Road 1515");
        em.persist(addr);
        addr = new AddressJPA(16, "P P", "P Road 1616");
        em.persist(addr);
        em.getTransaction().commit();
        em.close();
        emf.close();
    }
}

Build and run:

$ javac -cp 'hibpath' InsertAfter6.java AddressJPA.java
$ java -cp .:'hibpath':/javalib/sqlite-jdbc-3_14_1-vms.jar:/javalib/sqlitehib.jar "InsertAfter6"

List:

address.h:

struct address
{
    int id;
    char name[33];
    char address[65];
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "sqlite3.h"

#include "address.h"

int main()
{
    sqlite3 *con;
    sqlite3_stmt *sel;
    char *sqlstr = "SELECT id,name,address FROM address";
    int stat;
    int count;
    struct address res[1000];
    int i;
    stat = sqlite3_open("address.db", &con);
    stat = sqlite3_prepare(con, sqlstr, strlen(sqlstr), &sel, NULL);
    count = 0;
    while(sqlite3_step(sel) == SQLITE_ROW)
    {
        count++;
        res[count-1].id = sqlite3_column_int(sel, 0);
        strcpy(res[count-1].name, (char *)sqlite3_column_text(sel, 1));
        strcpy(res[count-1].address, (char *)sqlite3_column_text(sel, 2));
    }
    sqlite3_finalize(sel);
    sqlite3_close(con);
    for(i = 0; i < count; i++)
    {
        printf("%d|%s|%s\n", res[i].id, res[i].name, res[i].address);
    }
    return 0;
}

Build and run:

$ cc/include=sqlite3_include: list_after_1
$ link list_after_1 + sys$input/opt
sqlite3shr/share
$
$ run list_after_1

address.pas:

type
   address = record
                id : unsigned;
                name : varying[32] of char;
                address : varying[64] of char;
             end;

[inherit('psqlitedir:sqlite', 'psqlitedir:psqlite')]
program list_after_2(input, output);

%include 'address.pas'

var
   con : sqlite_ptr;
   sel : sqlite_stmt_ptr;
   count : integer;
   res : array [1..1000] of address;
   i : integer;

begin
   con := psqlite_open('address.db');
   sel := psqlite_prepare(con, 'SELECT id,name,address FROM address');
   count := 0;
   while psqlite_step_query(sel) do begin
      count := count + 1;
      res[count].id := psqlite_column_int(sel, 0);
      res[count].name := psqlite_column_text(sel, 1);
      res[count].address := psqlite_column_text(sel, 2);
   end;
   psqlite_finalize(sel);
   psqlite_close(con);
   for i := 1 to count do begin
      writeln(res[i].id, '|', res[i].name, '|', res[i].address);
   end;
end.

Build and run:

$ pas list_after_2
$ link list_after_2 + sys$input/opt + psqlitedir:sqlite/opt
psqlitedir:psqlite
psqlitedir:sqlite
$
$ run list_after_2

address.py:

class Address(object):
    def __init__(self, _id = 0, _name = '', _address = ''):
        self.id = _id
        self.name = _name
        self.address = _address

import sqlite3

from address import Address

con = sqlite3.connect('address.db')
c = con.cursor()
c.execute('SELECT id,name,address FROM address')
res = []
for row in c.fetchall():
    res.append(Address(row[0], row[1], row[2]))
con.commit()
con.close()

for o in res:
    print('%d|%s|%s' % (o.id, o.name, o.address))

address.php:

<?php
class Address {
    public $id;
    public $name;
    public $address;
    public function __construct($id = 0, $name = '', $address = '') {
        $this->id = $id;
        $this->name = $name;
        $this->address = $address;
    }
}
?>
<?php
include '/disk2/arne/migr/simple/address.php';

$con = new PDO('sqlite:/disk2/arne/migr/simple/address.db');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$stmt = $con->prepare('SELECT id,name,address FROM address');
$stmt->execute(array());
$res = array();
while($row = $stmt->fetch()) {
   $res[] = new Address($row['id'], $row['name'], $row['address']);
}

foreach($res as $o) {
    echo sprintf('%d|%s|%s', $o->id, $o->name, $o-> address) . "\r\n";
}
?>
public class Address {
    private int id;
    private String name;
    private String address;
    public Address() {
        this(0, "", "");
    }
    public Address(int id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class ListAfter5 {
    public static void main(String[] args) throws Exception {
        Class.forName("org.sqlite.JDBC");
        List<Address> res = new ArrayList<Address>();
        Connection con = DriverManager.getConnection("jdbc:sqlite:address.db");
        Statement stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT id,name,address FROM address");
        while(rs.next()) {
            int id = rs.getInt(1);
            String name = rs.getString(2);
            String address = rs.getString(3);
            res.add(new Address(id, name, address));
        }
        rs.close();
        stmt.close();
        con.close();
        for(Address o : res) {
            System.out.printf("%d|%s|%s\n", o.getId(), o.getName(), o.getAddress());
        }
    }
}

Build and run:

$ javac ListAfter5.java Address.java
$ java -cp .:/javalib/sqlite-jdbc-3_14_1-vms.jar "ListAfter5"
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="address")
public class AddressJPA {
    private int id;
    private String name;
    private String address;
    public AddressJPA() {
        this(0, "", "");
    }
    public AddressJPA(int id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="name")
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Column(name="address")
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;

public class ListAfter6 {
    public static void main(String[] args) throws Exception {
        Logger.getLogger("org.hibernate").setLevel(Level.OFF);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Address");
        EntityManager em = emf.createEntityManager();
        TypedQuery<AddressJPA> q = em.createQuery("SELECT o FROM AddressJPA AS o", AddressJPA.class);
        List<AddressJPA> res = q.getResultList();
        for(AddressJPA o : res) {
            System.out.printf("%d|%s|%s\n", o.getId(), o.getName(), o.getAddress());
        }
        em.close();
        emf.close();
    }
}

Build and run:

$ javac -cp 'hibpath' ListAfter6.java AddressJPA.java
$ java -cp .:'hibpath':/javalib/sqlite-jdbc-3_14_1-vms.jar:/javalib/sqlitehib.jar "ListAfter6"

Relation example (array of subrecords in record):

Data model:

Before records:

id customer no orderlines orderline item orderline qty orderline item orderline qty
1 A A 1 Something 2
2 B B 2 Something else 1 Something different 1

After database tables:

order

id customer
1 A A
2 B B

orderline

id orderid item qty
1 1 Something 2
2 2 Something else 1
3 2 Something different 1

Before:

Insert:

IDENTIFICATION DIVISION.
PROGRAM-ID.INSERT-BEFORE-1.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT OPTIONAL ORDER-FILE ASSIGN TO "order.isq" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS ORDER-ID.

DATA DIVISION.
FILE SECTION.
FD ORDER-FILE.
01 ORDER-RECORD.
    03 ORDER-ID PIC 9(8) COMP.
    03 ORDER-CUSTOMER PIC X(32).
    03 ORDER-NLINES PIC 9(8) COMP.
    03 LINES-ARRAY OCCURS 10 TIMES.
       05 ORDERLINE-ITEM PIC X(32).
       05 ORDERLINE-QTY PIC 9(8) COMP.
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
    OPEN I-O ORDER-FILE
    MOVE 1 TO ORDER-ID
    MOVE "A A" TO ORDER-CUSTOMER
    MOVE 1 TO ORDER-NLINES
    MOVE "Something" TO ORDERLINE-ITEM(1)
    MOVE 2 TO ORDERLINE-QTY(1)
    PERFORM INSERT-PARAGRAPH
    MOVE 2 TO ORDER-ID
    MOVE "B B" TO ORDER-CUSTOMER
    MOVE 2 TO ORDER-NLINES
    MOVE "Something else" TO ORDERLINE-ITEM(1)
    MOVE 1 TO ORDERLINE-QTY(1)
    MOVE "Something different" TO ORDERLINE-ITEM(2)
    MOVE 1 TO ORDERLINE-QTY(2)
    PERFORM INSERT-PARAGRAPH
    CLOSE ORDER-FILE
    STOP RUN.
INSERT-PARAGRAPH.
    WRITE ORDER-RECORD
        INVALID KEY DISPLAY "Error writing"
        NOT INVALID KEY CONTINUE
    END-WRITE.

Build and run:

$ cob insert_before_1
$ link insert_before_1
$ run insert_before_1

order_isam.pas

type
   orderline = record
                  item : packed array [1..32] of char;
                  qty : unsigned;
               end;
   order = record
              id : [key(0)]unsigned;
              customer : packed array [1..32] of char;
              nlines : integer;
              line : array [1..10] of orderline;
           end;
program insert_before_2(input, output);

%include 'order_isam.pas'

var
   o : order;
   db : file of order;

begin
   open(db, 'order.isq', unknown, organization := indexed, access_method := keyed);
   o.id := 3;
   o.customer := 'C C';
   o.nlines := 1;
   o.line[1].item := 'Thingy';
   o.line[1].qty := 1;
   db^ := o;
   put(db);
   o.id := 4;
   o.customer := 'D D';
   o.nlines := 2;
   o.line[1].item := 'Big thing';
   o.line[1].qty := 1;
   o.line[2].item := 'Small thing';
   o.line[2].qty := 3;
   db^ := o;
   put(db);
   close(db);
end.

Build and run:

$ pas insert_before_2
$ link insert_before_2
$ run insert_before_2

List:

IDENTIFICATION DIVISION.
PROGRAM-ID.LIST-BEFORE-1.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT OPTIONAL ORDER-FILE ASSIGN TO "order.isq" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS ORDER-ID.

DATA DIVISION.
FILE SECTION.
FD ORDER-FILE.
01 ORDER-RECORD.
    03 ORDER-ID PIC 9(8) COMP.
    03 ORDER-CUSTOMER PIC X(32).
    03 ORDER-NLINES PIC 9(8) COMP.
    03 LINES-ARRAY OCCURS 10 TIMES.
       05 ORDERLINE-ITEM PIC X(32).
       05 ORDERLINE-QTY PIC 9(8) COMP.
WORKING-STORAGE SECTION.
01 EOF-FLAG PIC X.         
01 ID PIC 9(8) DISPLAY.
01 QTY PIC 9(8) DISPLAY.
01 I PIC S9(9).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
    OPEN I-O ORDER-FILE
    MOVE 'N' TO EOF-FLAG
    PERFORM UNTIL EOF-FLAG = 'Y'
      READ ORDER-FILE NEXT
        AT END MOVE 'Y' TO EOF-FLAG
        NOT AT END PERFORM SHOW-PARAGRAPH
      END-READ
    END-PERFORM
    CLOSE ORDER-FILE
    STOP RUN.
SHOW-PARAGRAPH.
    MOVE ORDER-ID TO ID
    DISPLAY ID " " ORDER-CUSTOMER
    PERFORM VARYING I FROM 1 BY 1 UNTIL I > ORDER-NLINES    
      MOVE ORDERLINE-QTY(I) TO QTY
      DISPLAY "  " ORDERLINE-ITEM(I) ": " QTY
    END-PERFORM.

Build and run:

$ cob list_before_1
$ link list_before_1
$ run list_before_1

order_isam.pas

type
   orderline = record
                  item : packed array [1..32] of char;
                  qty : unsigned;
               end;
   order = record
              id : [key(0)]unsigned;
              customer : packed array [1..32] of char;
              nlines : integer;
              line : array [1..10] of orderline;
           end;
program list_before_2(input, output);

%include 'order_isam.pas'

var
   o : order;
   db : file of order;
   i : integer;

begin
   open(db, 'order.isq', unknown, organization := indexed, access_method := keyed);
   resetk(db, 0);
   while not eof(db) do begin
      o := db^;
      writeln(o.id:1,' ',o.customer);
      for i := 1 to o.nlines do begin
         writeln('  ',o.line[i].item,': ',o.line[i].qty);
      end;
      get(db);
   end;
   close(db);
end.

Build and run:

$ pas list_before_2
$ link list_before_2
$ run list_before_2

Convert:

The multiple subrecords within a record require two new features in the annotations:

@StructField(n=x, type=FieldType.STRUCT)
to indicate that the field is another struct (and the Java type is not a simple type but a Java class with annotations
@ArrayField(elements=x)
to indicate that the field is an array with the specified number of elements
import dk.vajhoej.record.Alignment;
import dk.vajhoej.record.Endian;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct(endianess=Endian.LITTLE, alignment=Alignment.ALIGN1)
public class OrderLineISAM {
    @StructField(n=0, type=FieldType.FIXSTR, length=32)
    private String item;
    @StructField(n=1, type=FieldType.INT4)
    private int qty;
    public OrderLineISAM() {
        this("", 0);
    }
    public OrderLineISAM(String item, int qty) {
        this.item = item;
        this.qty = qty;
    }
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import dk.vajhoej.isam.KeyField;
import dk.vajhoej.record.Alignment;
import dk.vajhoej.record.ArrayField;
import dk.vajhoej.record.Endian;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct(endianess=Endian.LITTLE, alignment=Alignment.ALIGN1)
public class OrderISAM {
    @KeyField(n=0)
    @StructField(n=0, type=FieldType.INT4)
    private int id;
    @StructField(n=1, type=FieldType.FIXSTR, length=32)
    private String customer;
    @StructField(n=2, type=FieldType.INT4)
    private int nlines;
    @ArrayField(elements=10)
    @StructField(n=3, type=FieldType.STRUCT)
    private OrderLineISAM[] line;
    public OrderISAM() {
        this(0, "");
    }
    public OrderISAM(int id, String customer) {
        this.id = id;
        this.customer = customer;
        nlines = 0;
        line = new OrderLineISAM[10];
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    public int getNLines() {
        return nlines;
    }
    public OrderLineISAM getLine(int ix) {
        return line[ix];
    }
}

The relation requires a new feature in the annotations:

@OneToMany
to indicate the one-to-many relation
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="orderline")
public class OrderLineJPA {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLineJPA() {
        this(0, 0, "", 0);
    }
    public OrderLineJPA(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="orderid")
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    @Column(name="item")
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    @Column(name="qty")
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="myorder")
public class OrderJPA {
    private int id;
    private String customer;
    private List<OrderLineJPA> lines;
    public OrderJPA() {
        this(0, "", new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer) {
        this(id, customer, new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer, List<OrderLineJPA> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="customer")
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    @OneToMany(mappedBy = "orderId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    public List<OrderLineJPA> getLines() {
        return lines;
    }
    public void setLines(List<OrderLineJPA> lines) {
        this.lines = lines;
    }
}
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class AutoMap<TFROM,TTO> {
    public static class MethodPair {
        private Method getter;
        private Method setter;
        private boolean trim;
        public MethodPair(Method getter, Method setter, boolean trim) {
            this.getter = getter;
            this.setter = setter;
            this.trim = trim;
        }
        public Method getGetter() {
            return getter;
        }
        public Method getSetter() {
            return setter;
        }
        public boolean getTrim() {
            return trim;
        }
    }
    private List<MethodPair> conv;
    public AutoMap(Class<TFROM> from, Class<TTO> to) throws IntrospectionException {
        this(from, to, false);
    }
    public AutoMap(Class<TFROM> from, Class<TTO> to, boolean trim) throws IntrospectionException {
        conv = new ArrayList<MethodPair>();
        for(PropertyDescriptor pdfrom : Introspector.getBeanInfo(from).getPropertyDescriptors()) {
            for(PropertyDescriptor pdto : Introspector.getBeanInfo(to).getPropertyDescriptors()) {
                if(pdfrom.getName().equals(pdto.getName())) {
                    Method getter = pdfrom.getReadMethod();
                    Method setter = pdto.getWriteMethod();
                    if(getter != null && setter != null) {
                        conv.add(new MethodPair(getter, setter, trim && pdfrom.getPropertyType().equals(String.class)));
                    }
                }
            }
        }
    }
    public void convert(TFROM from, TTO to) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        for(MethodPair mp : conv) {
            if(mp.getTrim()) {
                mp.getSetter().invoke(to, ((String)mp.getGetter().invoke(from)).trim());
            } else {
                mp.getSetter().invoke(to, mp.getGetter().invoke(from));
            }
        }
    }
}
# stuff for JPA
from java.util.logging import Level
from java.util.logging import Logger
from javax.persistence import Persistence

# stuff for ISAM
from dk.vajhoej.isam import Key0
from dk.vajhoej.isam.local import LocalIsamSource

#input class
import OrderISAM
import OrderLineISAM
#output class
import OrderJPA
import OrderLineJPA
# auto map
import AutoMap

# open JPA
Logger.getLogger("org.hibernate").setLevel(Level.OFF)
emf = Persistence.createEntityManagerFactory("Order")
em = emf.createEntityManager()

# open ISAM
db = LocalIsamSource('order.isq', 'dk.vajhoej.vms.rms.IndexSequential', False)

# process all records
omapper = AutoMap(OrderISAM, OrderJPA, True)
olmapper = AutoMap(OrderLineISAM, OrderLineJPA, True)
it = db.readGE(OrderISAM, Key0(0))
em.getTransaction().begin()
while it.read():
    oldrec = it.current()
    newrec = OrderJPA()
    omapper.convert(oldrec, newrec)
    for i in range(oldrec.getNLines()):
        newsubrec = OrderLineJPA()
        newsubrec.id = newrec.id * 100 + i
        newsubrec.orderId = newrec.id
        olmapper.convert(oldrec.getLine(i), newsubrec)
        newrec.lines.add(newsubrec)
    em.persist(newrec)
it.close()
em.getTransaction().commit()

# close ISAM
db.close()

# close JPA
em.close()
emf.close()

META-INF/persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="Order">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <class>OrderJPA</class>
      <class>OrderLineJPA</class>
      <exclude-unlisted-classes/>
      <properties>
          <!-- <property name="show_sql" value="true"/> -->
          <property name="hibernate.connection.driver_class" value="org.sqlite.JDBC"/>
          <property name="hibernate.connection.url" value="jdbc:sqlite:order.db"/>
          <property name="hibernate.connection.username" value=""/>
          <property name="hibernate.connection.password" value=""/>
          <property name="hibernate.dialect" value="org.hibernate.dialect.SQLiteDialect"/>
      </properties>
   </persistence-unit>
</persistence>

Database structure:

CREATE TABLE myorder (
    id INTEGER NOT NULL,
    customer VARCHAR(32),
    PRIMARY KEY(id)
);
CREATE TABLE orderline (
    id INTEGER NOT NULL,
    orderid INTEGER NOT NULL,
    item VARCHAR(32),
    qty INTEGER,
    PRIMARY KEY(id)
);

After:

Insert:

order.h:

struct orderline
{
    int id;
    int orderid;
    char item[33];
    int qty;
};

struct order
{
    int id;
    char customer[33];
    int nlines;
    struct orderline line[100];    
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "sqlite3.h"

#include "order.h"

int main()
{
    sqlite3 *con;
    sqlite3_stmt *oins, *olins;
    char *osqlstr = "INSERT INTO myorder VALUES(?,?)";
    char *olsqlstr = "INSERT INTO orderline VALUES(?,?,?,?)";
    int stat, i;
    struct order o;
    stat = sqlite3_open("order.db", &con);
    stat = sqlite3_prepare(con, osqlstr, strlen(osqlstr), &oins, NULL);
    o.id = 5;
    strcpy(o.customer, "E E");
    o.nlines = 1;
    o.line[0].id = 500;
    o.line[0].orderid = 5;
    strcpy(o.line[0].item, "A good C book");
    o.line[0].qty = 1;
    for(i = 0; i < o.nlines; i++)
    {
        stat = sqlite3_prepare(con, olsqlstr, strlen(olsqlstr), &olins, NULL);
        sqlite3_bind_int(olins, 1, o.line[i].id);
        sqlite3_bind_int(olins, 2, o.line[i].orderid);
        sqlite3_bind_text(olins, 3, o.line[0].item, strlen(o.line[i].item), NULL);
        sqlite3_bind_int(olins, 4, o.line[i].qty);
        stat = sqlite3_step(olins);
        sqlite3_finalize(olins);
    }
    sqlite3_bind_int(oins, 1, o.id);
    sqlite3_bind_text(oins, 2, o.customer, strlen(o.customer), NULL);
    stat = sqlite3_step(oins);
    sqlite3_finalize(oins);
    stat = sqlite3_prepare(con, osqlstr, strlen(osqlstr), &oins, NULL);
    o.id = 6;
    strcpy(o.customer, "F F");
    o.nlines = 1;
    o.line[0].id = 600;
    o.line[0].orderid = 6;
    strcpy(o.line[0].item, "Another good C book");
    o.line[0].qty = 1;
    for(i = 0; i < o.nlines; i++)
    {
        stat = sqlite3_prepare(con, olsqlstr, strlen(olsqlstr), &olins, NULL);
        sqlite3_bind_int(olins, 1, o.line[i].id);
        sqlite3_bind_int(olins, 2, o.line[i].orderid);
        sqlite3_bind_text(olins, 3, o.line[0].item, strlen(o.line[i].item), NULL);
        sqlite3_bind_int(olins, 4, o.line[i].qty);
        stat = sqlite3_step(olins);
        sqlite3_finalize(olins);
    }
    sqlite3_bind_int(oins, 1, o.id);
    sqlite3_bind_text(oins, 2, o.customer, strlen(o.customer), NULL);
    stat = sqlite3_step(oins);
    sqlite3_finalize(oins);
    sqlite3_close(con);
    return 0;
}

Build and run:

$ cc/include=sqlite3_include: insert_after_1
$ link insert_after_1 + sys$input/opt
sqlite3shr/share
$
$ run insert_after_1

order.pas:

type
   orderline = record
                  id : integer;
                  orderid : integer;
                  item : varying[32] of char;
                  qty : unsigned;
               end;
   order = record
              id : unsigned;
              customer : varying[32] of char;
              nlines : integer;
              line : array [1..100] of orderline;
           end;
[inherit('psqlitedir:sqlite', 'psqlitedir:psqlite')]
program insert_after_2(input, output);

%include 'order.pas'

var
   con : sqlite_ptr;
   oins, olins : sqlite_stmt_ptr;
   o : order;
   i : integer;

begin
   con := psqlite_open('order.db');
   oins := psqlite_prepare(con, 'INSERT INTO myorder VALUES(?,?)');
   o.id := 7;
   o.customer := 'G G';
   o.nlines := 1;
   o.line[1].id := 700;
   o.line[1].orderid := 7;
   o.line[1].item := 'A good Pascal book';
   o.line[1].qty := 1;
   for i := 1 to o.nlines do begin
      olins := psqlite_prepare(con, 'INSERT INTO orderline VALUES(?,?,?,?)');
      psqlite_bind_int(olins, 1, o.line[i].id);
      psqlite_bind_int(olins, 2, o.line[i].orderid);
      psqlite_bind_text(olins, 3, o.line[i].item);
      psqlite_bind_int(olins, 4, o.line[i].qty);
      psqlite_step_nonquery(olins);
      psqlite_finalize(olins);
   end;
   psqlite_bind_int(oins, 1, o.id);
   psqlite_bind_text(oins, 2, o.customer);
   psqlite_step_nonquery(oins);
   psqlite_finalize(oins);
   oins := psqlite_prepare(con, 'INSERT INTO myorder VALUES(?,?)');
   o.id := 8;
   o.customer := 'H H';
   o.nlines := 1;
   o.line[1].id := 800;
   o.line[1].orderid := 8;
   o.line[1].item := 'Another good Pascal book';
   o.line[1].qty := 1;
   for i := 1 to o.nlines do begin
      olins := psqlite_prepare(con, 'INSERT INTO orderline VALUES(?,?,?,?)');
      psqlite_bind_int(olins, 1, o.line[i].id);
      psqlite_bind_int(olins, 2, o.line[i].orderid);
      psqlite_bind_text(olins, 3, o.line[i].item);
      psqlite_bind_int(olins, 4, o.line[i].qty);
      psqlite_step_nonquery(olins);
      psqlite_finalize(olins);
   end;
   psqlite_bind_int(oins, 1, o.id);
   psqlite_bind_text(oins, 2, o.customer);
   psqlite_step_nonquery(oins);
   psqlite_finalize(oins);
   psqlite_close(con);
end.

Build and run:

$ pas insert_after_2
$ link insert_after_2 + sys$input/opt + psqlitedir:sqlite/opt
psqlitedir:psqlite
psqlitedir:sqlite
$
$ run insert_after_2

order.py:

class OrderLine(object):
    def __init__(self, _id = 0, _orderid = 0, _item = '', _qty = 0):
        self.id = _id
        self.orderid = _orderid
        self.item = _item
        self.qty = _qty

class Order(object):
    def __init__(self, _id = 0, _customer = '', _lines = []):
        self.id = _id
        self.customer = _customer
        self.lines = _lines
import sqlite3

from order import Order, OrderLine

con = sqlite3.connect('order.db')
c = con.cursor()
o = Order(9, 'I I', [ OrderLine(900, 9, 'A good Python book', 1) ]);
for ol in o.lines:
    c.execute('INSERT INTO orderline VALUES(?,?,?,?)', (ol.id, ol.orderid, ol.item, ol.qty))
c.execute('INSERT INTO myorder VALUES(?,?)', (o.id, o.customer))
o = Order(10, 'J J', [ OrderLine(1000, 10, 'Another good Python book', 1) ]);
for ol in o.lines:
    c.execute('INSERT INTO orderline VALUES(?,?,?,?)', (ol.id, ol.orderid, ol.item, ol.qty))
c.execute('INSERT INTO myorder VALUES(?,?)', (o.id, o.customer))
con.commit()
con.close()

order.php:

<?php
class OrderLine {
    public $id;
    public $orderid;
    public $item;
    public $qty;
    public function __construct($id = 0, $orderid = 0, $item = '', $qty = 0) {
        $this->id = $id;
        $this->orderid = $orderid;
        $this->item = $item;
        $this->qty = $qty;
    }
}

class Order {
    public $id;
    public $customer;
    public $lines;
    public function __construct($id = 0, $customer = '', $lines = array()) {
        $this->id = $id;
        $this->customer = $customer;
        $this->lines = $lines;
    }
}

?>
<?php
include '/disk2/arne/migr/relsub/order.php';

$con = new PDO('sqlite:/disk2/arne/migr/relsub/order.db');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$ostmt = $con->prepare('INSERT INTO myorder VALUES(:ID,:CUSTOMER)');
$olstmt = $con->prepare('INSERT INTO orderline VALUES(:ID,:ORDERID,:ITEM,:QTY)');
$o = new Order(11, 'K K', array(new OrderLine(1100, 11, 'A good PHP book', 1)));
foreach($o->lines as $ol) {
    $olstmt->execute(array(':ID' => $ol->id, ':ORDERID' => $ol->orderid, ':ITEM' => $ol->item, ':QTY' => $ol->qty));
}
$ostmt->execute(array(':ID' => $o->id, ':CUSTOMER' => $o->customer));
$o = new Order(12, 'L L', array(new OrderLine(1200, 12, 'Another good PHP book', 1)));
foreach($o->lines as $ol) {
    $olstmt->execute(array(':ID' => $ol->id, ':ORDERID' => $ol->orderid, ':ITEM' => $ol->item, ':QTY' => $ol->qty));
}
$ostmt->execute(array(':ID' => $o->id, ':CUSTOMER' => $o->customer));
?>
public class OrderLine {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLine() {
        this(0, 0, "", 0);
    }
    public OrderLine(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

public class Order {
    private int id;
    private String customer;
    private List<OrderLine> lines;
    public Order() {
        this(0, "", new ArrayList<OrderLine>());
    }
    public Order(int id, String customer) {
        this(id, customer, new ArrayList<OrderLine>());
    }
    public Order(int id, String customer, List<OrderLine> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    public List<OrderLine> getLines() {
        return lines;
    }
    public void setLines(List<OrderLine> lines) {
        this.lines = lines;
    }
}
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;

public class InsertAfter5 {
    public static void main(String[] args) throws Exception {
        Class.forName("org.sqlite.JDBC");
        Connection con = DriverManager.getConnection("jdbc:sqlite:order.db");
        PreparedStatement opstmt = con.prepareStatement("INSERT INTO myorder VALUES(?,?)");
        PreparedStatement olpstmt = con.prepareStatement("INSERT INTO orderline VALUES(?,?,?,?)");
        Order o;
        o = new Order(13, "M M");
        o.getLines().add(new OrderLine(1300, 13, "A good Java book", 1));
        for(OrderLine ol : o.getLines()) {
            olpstmt.setInt(1, ol.getId());
            olpstmt.setInt(2, ol.getOrderId());
            olpstmt.setString(3, ol.getItem());
            olpstmt.setInt(4, ol.getQty());
            olpstmt.executeUpdate();
        }
        opstmt.setInt(1, o.getId());
        opstmt.setString(2, o.getCustomer());
        opstmt.executeUpdate();
        o = new Order(14, "N N");
        o.getLines().add(new OrderLine(1400, 14, "Another good Java book", 1));
        for(OrderLine ol : o.getLines()) {
            olpstmt.setInt(1, ol.getId());
            olpstmt.setInt(2, ol.getOrderId());
            olpstmt.setString(3, ol.getItem());
            olpstmt.setInt(4, ol.getQty());
            olpstmt.executeUpdate();
        }
        opstmt.setInt(1, o.getId());
        opstmt.setString(2, o.getCustomer());
        opstmt.executeUpdate();
        opstmt.close();
        con.close();
    }
}

Build and run:

$ javac InsertAfter5.java Order.java OrderLine.java
$ java -cp .:/javalib/sqlite-jdbc-3_14_1-vms.jar "InsertAfter5"
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="orderline")
public class OrderLineJPA {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLineJPA() {
        this(0, 0, "", 0);
    }
    public OrderLineJPA(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="orderid")
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    @Column(name="item")
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    @Column(name="qty")
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="myorder")
public class OrderJPA {
    private int id;
    private String customer;
    private List<OrderLineJPA> lines;
    public OrderJPA() {
        this(0, "", new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer) {
        this(id, customer, new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer, List<OrderLineJPA> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="customer")
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    @OneToMany(mappedBy = "orderId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    public List<OrderLineJPA> getLines() {
        return lines;
    }
    public void setLines(List<OrderLineJPA> lines) {
        this.lines = lines;
    }
}
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;

public class InsertAfter6 {
    public static void main(String[] args) throws Exception {
        Logger.getLogger("org.hibernate").setLevel(Level.OFF);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Order");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        OrderJPA o;
        o = new OrderJPA(15, "O O");
        o.getLines().add(new OrderLineJPA(1500, 15, "A good Java book", 1));
        em.persist(o);
        o = new OrderJPA(16, "P P");
        o.getLines().add(new OrderLineJPA(1600, 16, "Another good Java book", 1));
        em.persist(o);
        em.getTransaction().commit();
        em.close();
        emf.close();
    }
}

Build and run:

$ javac -cp 'hibpath' InsertAfter6.java OrderJPA.java OrderLineJPA.java
$ java -cp .:'hibpath':/javalib/sqlite-jdbc-3_14_1-vms.jar:/javalib/sqlitehib.jar "InsertAfter6"

List:

order.h:

struct orderline
{
    int id;
    int orderid;
    char item[33];
    int qty;
};

struct order
{
    int id;
    char customer[33];
    int nlines;
    struct orderline line[100];    
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "sqlite3.h"

#include "order.h"

int main()
{
    sqlite3 *con;
    sqlite3_stmt *sel;
    char *sqlstr = "SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid";
    int stat;
    int lastid;
    int count;
    struct order res[1000];
    int i, j;
    stat = sqlite3_open("order.db", &con);
    stat = sqlite3_prepare(con, sqlstr, strlen(sqlstr), &sel, NULL);
    lastid = -1;
    count = 0;
    while(sqlite3_step(sel) == SQLITE_ROW)
    {
        if(sqlite3_column_int(sel, 0) != lastid)
        {
            lastid = sqlite3_column_int(sel, 0);
            count++;
            res[count-1].id = sqlite3_column_int(sel, 0);
            strcpy(res[count-1].customer, (char *)sqlite3_column_text(sel, 1));
            res[count-1].nlines = 0;
        }
        res[count-1].line[res[count-1].nlines].id = sqlite3_column_int(sel, 2);
        res[count-1].line[res[count-1].nlines].orderid = sqlite3_column_int(sel, 3);
        strcpy(res[count-1].line[res[count-1].nlines].item, (char *)sqlite3_column_text(sel, 4));
        res[count-1].line[res[count-1].nlines].qty = sqlite3_column_int(sel, 5);
        res[count-1].nlines++;
    }
    sqlite3_finalize(sel);
    sqlite3_close(con);
    for(i = 0; i < count; i++)
    {
        printf("%d %s\n", res[i].id, res[i].customer);
        for(j = 0; j < res[i].nlines; j++)
        {
            printf("  %s : %d\n", res[i].line[j].item, res[i].line[j].qty);
        }
    }
    return 0;
}

Build and run:

$ cc/include=sqlite3_include: list_after_1
$ link list_after_1 + sys$input/opt
sqlite3shr/share
$
$ run list_after_1

order.pas:

type
   orderline = record
                  id : integer;
                  orderid : integer;
                  item : varying[32] of char;
                  qty : unsigned;
               end;
   order = record
              id : unsigned;
              customer : varying[32] of char;
              nlines : integer;
              line : array [1..100] of orderline;
           end;
[inherit('psqlitedir:sqlite', 'psqlitedir:psqlite')]
program list_after_2(input, output);

%include 'order.pas'

var
   con : sqlite_ptr;
   sel : sqlite_stmt_ptr;
   res : array [1..1000] of order;
   count : integer;
   lastid : integer;
   i, j : integer;

begin
   con := psqlite_open('order.db');
   sel := psqlite_prepare(con, 'SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid');
   count := 0;
   lastid := -1;
   while psqlite_step_query(sel) do begin
      if psqlite_column_int(sel, 0) <> lastid then begin
         lastid := psqlite_column_int(sel, 0);
         count := count + 1;
         res[count].id := psqlite_column_int(sel, 0);
         res[count].customer := psqlite_column_text(sel, 1);
         res[count].nlines := 0;
      end; 
      res[count].nlines := res[count].nlines + 1;
      res[count].line[res[count].nlines].id := psqlite_column_int(sel, 2);
      res[count].line[res[count].nlines].orderid := psqlite_column_int(sel, 3);
      res[count].line[res[count].nlines].item := psqlite_column_text(sel, 4);
      res[count].line[res[count].nlines].qty := psqlite_column_int(sel, 5);
   end;
   psqlite_finalize(sel);
   psqlite_close(con);
   for i := 1 to count do begin
      writeln(res[i].id:1, ' ', res[i].customer);
      for j := 1 to res[i].nlines do begin
         writeln('  ', res[i].line[j].item, ': ', res[i].line[j].qty);
      end;
   end;
end.

Build and run:

$ pas list_after_2
$ link list_after_2 + sys$input/opt + psqlitedir:sqlite/opt
psqlitedir:psqlite
psqlitedir:sqlite
$
$ run list_after_2

order.py:

class OrderLine(object):
    def __init__(self, _id = 0, _orderid = 0, _item = '', _qty = 0):
        self.id = _id
        self.orderid = _orderid
        self.item = _item
        self.qty = _qty

class Order(object):
    def __init__(self, _id = 0, _customer = '', _lines = []):
        self.id = _id
        self.customer = _customer
        self.lines = _lines
import sqlite3

from order import Order, OrderLine

con = sqlite3.connect('order.db')
c = con.cursor()
c.execute('SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid')
res = []
lastid = -1
lasto = None
for row in c.fetchall():
    if row[0] != lastid:
        lastid = row[0]
        lasto = Order(row[0], row[1], [])
        res.append(lasto)
    lasto.lines.append(OrderLine(row[2], row[3], row[4], row[5]))
con.commit()
con.close()

for o in res:
    print('%d %s' % (o.id, o.customer))
    for ol in o.lines:
        print('  %s : %d' % (ol.item, ol.qty))

order.php:

<?php
class OrderLine {
    public $id;
    public $orderid;
    public $item;
    public $qty;
    public function __construct($id = 0, $orderid = 0, $item = '', $qty = 0) {
        $this->id = $id;
        $this->orderid = $orderid;
        $this->item = $item;
        $this->qty = $qty;
    }
}

class Order {
    public $id;
    public $customer;
    public $lines;
    public function __construct($id = 0, $customer = '', $lines = array()) {
        $this->id = $id;
        $this->customer = $customer;
        $this->lines = $lines;
    }
}

?>
<?php
include '/disk2/arne/migr/relsub/order.php';

$con = new PDO('sqlite:/disk2/arne/migr/relsub/order.db');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_NUM);
$stmt = $con->prepare('SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid');
$stmt->execute(array());
$res = array();
$lastid = -1;
$lasto = null;
while($row = $stmt->fetch()) {
    if($lastid != $row[0]) {
        $lastid = $row[0];
        $lasto = new Order($row[0], $row[1], array());
        $res[] = $lasto;
    }
    $lasto->lines[] = new OrderLine($row[2], $row[3], $row[4], $row[5]);
}

foreach($res as $o) {
    echo sprintf('%d %s', $o->id, $o->customer) . "\r\n";
    foreach($o->lines as $ol) {
        echo sprintf('  %s : %d', $ol->item, $ol->qty) . "\r\n";
    }
}
?>
public class OrderLine {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLine() {
        this(0, 0, "", 0);
    }
    public OrderLine(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

public class Order {
    private int id;
    private String customer;
    private List<OrderLine> lines;
    public Order() {
        this(0, "", new ArrayList<OrderLine>());
    }
    public Order(int id, String customer) {
        this(id, customer, new ArrayList<OrderLine>());
    }
    public Order(int id, String customer, List<OrderLine> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    public List<OrderLine> getLines() {
        return lines;
    }
    public void setLines(List<OrderLine> lines) {
        this.lines = lines;
    }
}
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class ListAfter5 {
    public static void main(String[] args) throws Exception {
        Class.forName("org.sqlite.JDBC");
        List<Order> res = new ArrayList<Order>();
        Connection con = DriverManager.getConnection("jdbc:sqlite:order.db");
        Statement stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid");
        int lastid = -1;
        Order lasto = null;
        while(rs.next()) {
            int id = rs.getInt(1);
            String customer = rs.getString(2);
            int lineid = rs.getInt(3);
            int orderid = rs.getInt(4);
            String item = rs.getString(5);
            int qty = rs.getInt(6);
            if(id != lastid) {
                lastid = id;
                lasto = new Order(id, customer);
                res.add(lasto);
            }
            OrderLine line = new OrderLine(lineid, orderid, item, qty);
            lasto.getLines().add(line);
        }
        rs.close();
        stmt.close();
        con.close();
        for(Order o : res) {
            System.out.printf("%d %s\n", o.getId(), o.getCustomer());
            for(OrderLine ol : o.getLines()) {
                System.out.printf("  %s : %d\n", ol.getItem(), ol.getQty());
            }
        }
    }
}

Build and run:

$ javac ListAfter5.java Order.java OrderLine.java
$ java -cp .:/javalib/sqlite-jdbc-3_14_1-vms.jar "ListAfter5"
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="orderline")
public class OrderLineJPA {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLineJPA() {
        this(0, 0, "", 0);
    }
    public OrderLineJPA(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="orderid")
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    @Column(name="item")
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    @Column(name="qty")
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="myorder")
public class OrderJPA {
    private int id;
    private String customer;
    private List<OrderLineJPA> lines;
    public OrderJPA() {
        this(0, "", new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer) {
        this(id, customer, new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer, List<OrderLineJPA> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="customer")
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    @OneToMany(mappedBy = "orderId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    public List<OrderLineJPA> getLines() {
        return lines;
    }
    public void setLines(List<OrderLineJPA> lines) {
        this.lines = lines;
    }
}
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;

public class ListAfter6 {
    public static void main(String[] args) throws Exception {
        Logger.getLogger("org.hibernate").setLevel(Level.OFF);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Order");
        EntityManager em = emf.createEntityManager();
        TypedQuery<OrderJPA> q = em.createQuery("SELECT o FROM OrderJPA AS o JOIN FETCH o.lines ol", OrderJPA.class);
        List<OrderJPA> res = q.getResultList();
        for(OrderJPA o : res) {
            System.out.printf("%d %s\n", o.getId(), o.getCustomer());
            for(OrderLineJPA ol : o.getLines()) {
                System.out.printf("  %s : %d\n", ol.getItem(), ol.getQty());
            }
        }
        em.close();
        emf.close();
    }
}

Build and run:

$ javac -cp 'hibpath' ListAfter6.java OrderJPA.java OrderLineJPA.java
$ java -cp .:'hibpath':/javalib/sqlite-jdbc-3_14_1-vms.jar:/javalib/sqlitehib.jar "ListAfter6"

Relation example (two files - records and subrecords):

Data model:

Before records:

id customer
1 A A
2 B B
id orderid item qty
1 1 Something 2
2 2 Something else 1
3 2 Something different 1

After database tables:

order

id customer
1 A A
2 B B

orderline

id orderid item qty
1 1 Something 2
2 2 Something else 1
3 2 Something different 1

Before:

Insert:

IDENTIFICATION DIVISION.
PROGRAM-ID.INSERT-BEFORE-1.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT OPTIONAL ORDER-FILE ASSIGN TO "order.isq"
                               ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC
                               RECORD KEY IS ORDER-ID.
    SELECT OPTIONAL ORDERLINE-FILE ASSIGN TO "orderline.isq"
                                   ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC
                                   RECORD KEY IS ORDERLINE-ID
                                   ALTERNATE RECORD KEY IS ORDERLINE-ORDERID WITH DUPLICATES.
DATA DIVISION.
FILE SECTION.
FD ORDER-FILE.
01 ORDER-RECORD.
    03 ORDER-ID PIC 9(8) COMP.
    03 ORDER-CUSTOMER PIC X(32).
FD ORDERLINE-FILE.
01 ORDERLINE-RECORD.
    03 ORDERLINE-ID PIC 9(8) COMP.
    03 ORDERLINE-ORDERID PIC 9(8) COMP.
    03 ORDERLINE-ITEM PIC X(32).
    03 ORDERLINE-QTY PIC 9(8) COMP.
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
    OPEN I-O ORDER-FILE
    OPEN I-O ORDERLINE-FILE
    MOVE 1 TO ORDER-ID
    MOVE "A A" TO ORDER-CUSTOMER
    PERFORM INSERT-ORDER-PARAGRAPH
    MOVE 100 TO ORDERLINE-ID
    MOVE 1 TO ORDERLINE-ORDERID
    MOVE "Something" TO ORDERLINE-ITEM
    MOVE 2 TO ORDERLINE-QTY
    PERFORM INSERT-ORDERLINE-PARAGRAPH
    MOVE 2 TO ORDER-ID
    MOVE "B B" TO ORDER-CUSTOMER
    PERFORM INSERT-ORDER-PARAGRAPH
    MOVE 200 TO ORDERLINE-ID
    MOVE 2 TO ORDERLINE-ORDERID
    MOVE "Something else" TO ORDERLINE-ITEM
    MOVE 1 TO ORDERLINE-QTY
    PERFORM INSERT-ORDERLINE-PARAGRAPH
    MOVE 201 TO ORDERLINE-ID
    MOVE 2 TO ORDERLINE-ORDERID
    MOVE "Something different" TO ORDERLINE-ITEM
    MOVE 1 TO ORDERLINE-QTY
    PERFORM INSERT-ORDERLINE-PARAGRAPH
    CLOSE ORDER-FILE
    CLOSE ORDERLINE-FILE
    STOP RUN.
INSERT-ORDER-PARAGRAPH.
    WRITE ORDER-RECORD
        INVALID KEY DISPLAY "Error writing"
        NOT INVALID KEY CONTINUE
    END-WRITE.
INSERT-ORDERLINE-PARAGRAPH.
    WRITE ORDERLINE-RECORD
        INVALID KEY DISPLAY "Error writing"
        NOT INVALID KEY CONTINUE
    END-WRITE.

Build and run:

$ cob insert_before_1
$ link insert_before_1
$ run insert_before_1

order_isam.pas:

type
   orderline = record
                  id : [key(0)]unsigned;
                  orderid : [key(1)]unsigned;
                  item : packed array [1..32] of char;
                  qty : unsigned;
               end;
   order = record
              id : [key(0)]unsigned;
              customer : packed array [1..32] of char;
           end;
program insert_before_2(input, output);

%include 'order_isam.pas'

var
   o : order;
   odb : file of order;
   ol : orderline;
   oldb : file of orderline;

begin
   open(odb, 'order.isq', unknown, organization := indexed, access_method := keyed);
   open(oldb, 'orderline.isq', unknown, organization := indexed, access_method := keyed);
   o.id := 3;
   o.customer := 'C C';
   odb^ := o;
   put(odb);
   ol.id := 300;
   ol.orderid := 3;
   ol.item := 'Thingy';
   ol.qty := 1;
   oldb^ := ol;
   put(oldb);
   o.id := 4;
   o.customer := 'D D';
   odb^ := o;
   put(odb);
   ol.id := 400;
   ol.orderid := 4;
   ol.item := 'Big thing';
   ol.qty := 1;
   oldb^ := ol;
   put(oldb);
   ol.id := 401;
   ol.orderid := 4;
   ol.item := 'Small thing';
   ol.qty := 3;
   oldb^ := ol;
   put(oldb);
   close(odb);
   close(oldb);
end.

Build and run:

$ pas insert_before_2
$ link insert_before_2
$ run insert_before_2

List:

IDENTIFICATION DIVISION.
PROGRAM-ID.LIST-BEFORE-1.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT OPTIONAL ORDER-FILE ASSIGN TO "order.isq"
                               ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC
                               RECORD KEY IS ORDER-ID.
    SELECT OPTIONAL ORDERLINE-FILE ASSIGN TO "orderline.isq"
                                   ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC
                                   RECORD KEY IS ORDERLINE-ID
                                   ALTERNATE RECORD KEY IS ORDERLINE-ORDERID WITH DUPLICATES.
DATA DIVISION.
FILE SECTION.
FD ORDER-FILE.
01 ORDER-RECORD.
    03 ORDER-ID PIC 9(8) COMP.
    03 ORDER-CUSTOMER PIC X(32).
FD ORDERLINE-FILE.
01 ORDERLINE-RECORD.
    03 ORDERLINE-ID PIC 9(8) COMP.
    03 ORDERLINE-ORDERID PIC 9(8) COMP.
    03 ORDERLINE-ITEM PIC X(32).
    03 ORDERLINE-QTY PIC 9(8) COMP.
WORKING-STORAGE SECTION.
01 EOF-FLAG PIC X.         
01 DONE-FLAG PIC X.         
01 ID PIC 9(8) DISPLAY.
01 QTY PIC 9(8) DISPLAY.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
    OPEN I-O ORDER-FILE
    OPEN I-O ORDERLINE-FILE
    MOVE 'N' TO EOF-FLAG
    PERFORM UNTIL EOF-FLAG = 'Y'
      READ ORDER-FILE NEXT
        AT END MOVE 'Y' TO EOF-FLAG
        NOT AT END PERFORM SHOW-ORDER-PARAGRAPH
      END-READ
    END-PERFORM
    CLOSE ORDER-FILE
    CLOSE ORDERLINE-FILE
    STOP RUN.
SHOW-ORDER-PARAGRAPH.
    MOVE ORDER-ID TO ID
    DISPLAY ID " " ORDER-CUSTOMER
    MOVE ORDER-ID TO ORDERLINE-ORDERID
    START ORDERLINE-FILE KEY IS GREATER THAN OR EQUAL TO ORDERLINE-ORDERID
        INVALID KEY DISPLAY "Key problem"
        NOT INVALID KEY CONTINUE
    END-START
    MOVE 'N' TO EOF-FLAG
    MOVE 'N' TO DONE-FLAG
    PERFORM UNTIL EOF-FLAG = 'Y' OR DONE-FLAG = 'Y'
        READ ORDERLINE-FILE NEXT
            AT END MOVE 'Y' TO EOF-FLAG
            NOT AT END PERFORM ORDERLINE-PARAGRAPH
        END-READ
    END-PERFORM.
ORDERLINE-PARAGRAPH.
    IF ORDERLINE-ORDERID EQUAL TO ORDER-ID THEN
        PERFORM DISPLAY-ORDERLINE-PARAGRAPH
    ELSE
        MOVE 'Y' TO DONE-FLAG
    END-IF.
DISPLAY-ORDERLINE-PARAGRAPH.
    MOVE ORDERLINE-QTY TO QTY
    DISPLAY "  " ORDERLINE-ITEM ": " QTY.

Build and run:

$ cob list_before_1
$ link list_before_1
$ run list_before_1

order_isam.pas:

type
   orderline = record
                  id : [key(0)]unsigned;
                  orderid : [key(1)]unsigned;
                  item : packed array [1..32] of char;
                  qty : unsigned;
               end;
   order = record
              id : [key(0)]unsigned;
              customer : packed array [1..32] of char;
           end;
program list_before_2(input, output);

%include 'order_isam.pas'

var
   o : order;
   odb : file of order;
   ol : orderline;
   oldb : file of orderline;
   done : boolean;

begin
   open(odb, 'order.isq', unknown, organization := indexed, access_method := keyed);
   open(oldb, 'orderline.isq', unknown, organization := indexed, access_method := keyed);
   resetk(odb, 0);
   while not eof(odb) do begin
      o := odb^;
      writeln(o.id:1,' ',o.customer);
      findk(oldb, 1, o.id);
      done := false;
      while not eof(oldb) do begin
         ol := oldb^;
         if (ol.orderid = o.id) and not done then begin
            writeln('  ', ol.item, ': ', ol.qty);
         end else begin
            done := true;
         end;
         get(oldb);
      end;
      get(odb);
   end;
   close(odb);
   close(oldb);
end.

Build and run:

$ pas list_before_2
$ link list_before_2
$ run list_before_2

Convert:

import dk.vajhoej.isam.KeyField;
import dk.vajhoej.record.Alignment;
import dk.vajhoej.record.Endian;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct(endianess=Endian.LITTLE, alignment=Alignment.ALIGN1)
public class OrderLineISAM {
    @KeyField(n=0)
    @StructField(n=0, type=FieldType.INT4)
    private int id;
    @KeyField(n=1)
    @StructField(n=1, type=FieldType.INT4)
    private int orderId;
    @StructField(n=2, type=FieldType.FIXSTR, length=32)
    private String item;
    @StructField(n=3, type=FieldType.INT4)
    private int qty;
    public OrderLineISAM() {
        this(0, 0, "", 0);
    }
    public OrderLineISAM(int id, int orderId, String item, int qty) {
        this.id = qty;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import dk.vajhoej.isam.KeyField;
import dk.vajhoej.record.Alignment;
import dk.vajhoej.record.ArrayField;
import dk.vajhoej.record.Endian;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct(endianess=Endian.LITTLE, alignment=Alignment.ALIGN1)
public class OrderISAM {
    @KeyField(n=0)
    @StructField(n=0, type=FieldType.INT4)
    private int id;
    @StructField(n=1, type=FieldType.FIXSTR, length=32)
    private String customer;
    public OrderISAM() {
        this(0, "");
    }
    public OrderISAM(int id, String customer) {
        this.id = id;
        this.customer = customer;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
}

The relation requires a new feature in the annotations:

@OneToMany
to indicate the one-to-many relation
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="orderline")
public class OrderLineJPA {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLineJPA() {
        this(0, 0, "", 0);
    }
    public OrderLineJPA(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="orderid")
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    @Column(name="item")
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    @Column(name="qty")
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="myorder")
public class OrderJPA {
    private int id;
    private String customer;
    private List<OrderLineJPA> lines;
    public OrderJPA() {
        this(0, "", new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer) {
        this(id, customer, new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer, List<OrderLineJPA> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="customer")
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    @OneToMany(mappedBy = "orderId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    public List<OrderLineJPA> getLines() {
        return lines;
    }
    public void setLines(List<OrderLineJPA> lines) {
        this.lines = lines;
    }
}
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class AutoMap<TFROM,TTO> {
    public static class MethodPair {
        private Method getter;
        private Method setter;
        private boolean trim;
        public MethodPair(Method getter, Method setter, boolean trim) {
            this.getter = getter;
            this.setter = setter;
            this.trim = trim;
        }
        public Method getGetter() {
            return getter;
        }
        public Method getSetter() {
            return setter;
        }
        public boolean getTrim() {
            return trim;
        }
    }
    private List<MethodPair> conv;
    public AutoMap(Class<TFROM> from, Class<TTO> to) throws IntrospectionException {
        this(from, to, false);
    }
    public AutoMap(Class<TFROM> from, Class<TTO> to, boolean trim) throws IntrospectionException {
        conv = new ArrayList<MethodPair>();
        for(PropertyDescriptor pdfrom : Introspector.getBeanInfo(from).getPropertyDescriptors()) {
            for(PropertyDescriptor pdto : Introspector.getBeanInfo(to).getPropertyDescriptors()) {
                if(pdfrom.getName().equals(pdto.getName())) {
                    Method getter = pdfrom.getReadMethod();
                    Method setter = pdto.getWriteMethod();
                    if(getter != null && setter != null) {
                        conv.add(new MethodPair(getter, setter, trim && pdfrom.getPropertyType().equals(String.class)));
                    }
                }
            }
        }
    }
    public void convert(TFROM from, TTO to) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        for(MethodPair mp : conv) {
            if(mp.getTrim()) {
                mp.getSetter().invoke(to, ((String)mp.getGetter().invoke(from)).trim());
            } else {
                mp.getSetter().invoke(to, mp.getGetter().invoke(from));
            }
        }
    }
}
# stuff for JPA
from java.util.logging import Level
from java.util.logging import Logger
from javax.persistence import Persistence

# stuff for ISAM
from dk.vajhoej.isam import Key0
from dk.vajhoej.isam import Key1
from dk.vajhoej.isam.local import LocalIsamSource

#input class
import OrderISAM
import OrderLineISAM
#output class
import OrderJPA
import OrderLineJPA
# auto map
import AutoMap

# open JPA
Logger.getLogger("org.hibernate").setLevel(Level.OFF)
emf = Persistence.createEntityManagerFactory("Order")
em = emf.createEntityManager()

# open ISAM
odb = LocalIsamSource('order.isq', 'dk.vajhoej.vms.rms.IndexSequential', False)
oldb = LocalIsamSource('orderline.isq', 'dk.vajhoej.vms.rms.IndexSequential', False)

# process all records
omapper = AutoMap(OrderISAM, OrderJPA, True)
olmapper = AutoMap(OrderLineISAM, OrderLineJPA, True)
it = odb.readGE(OrderISAM, Key0(0))
em.getTransaction().begin()
while it.read():
    oldrec = it.current()
    newrec = OrderJPA()
    omapper.convert(oldrec, newrec)
    it2 = oldb.readGE(OrderLineISAM, Key1(oldrec.id))
    while it2.read():
        oldsubrec = it2.current()
        if oldsubrec.orderId == oldrec.id:
           newsubrec = OrderLineJPA()
           olmapper.convert(oldsubrec, newsubrec)
           newrec.lines.add(newsubrec)
        else:
           break
    em.persist(newrec)
it.close()
em.getTransaction().commit()

# close ISAM
odb.close()
oldb.close()

# close JPA
em.close()
emf.close()

META-INF/persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="Order">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <class>OrderJPA</class>
      <class>OrderLineJPA</class>
      <exclude-unlisted-classes/>
      <properties>
          <!-- <property name="show_sql" value="true"/> -->
          <property name="hibernate.connection.driver_class" value="org.sqlite.JDBC"/>
          <property name="hibernate.connection.url" value="jdbc:sqlite:order.db"/>
          <property name="hibernate.connection.username" value=""/>
          <property name="hibernate.connection.password" value=""/>
          <property name="hibernate.dialect" value="org.hibernate.dialect.SQLiteDialect"/>
      </properties>
   </persistence-unit>
</persistence>

Database structure:

CREATE TABLE myorder (
    id INTEGER NOT NULL,
    customer VARCHAR(32),
    PRIMARY KEY(id)
);
CREATE TABLE orderline (
    id INTEGER NOT NULL,
    orderid INTEGER NOT NULL,
    item VARCHAR(32),
    qty INTEGER,
    PRIMARY KEY(id)
);

After:

Insert:

order.h:

struct orderline
{
    int id;
    int orderid;
    char item[33];
    int qty;
};

struct order
{
    int id;
    char customer[33];
    int nlines;
    struct orderline line[100];    
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "sqlite3.h"

#include "order.h"

int main()
{
    sqlite3 *con;
    sqlite3_stmt *oins, *olins;
    char *osqlstr = "INSERT INTO myorder VALUES(?,?)";
    char *olsqlstr = "INSERT INTO orderline VALUES(?,?,?,?)";
    int stat, i;
    struct order o;
    stat = sqlite3_open("order.db", &con);
    stat = sqlite3_prepare(con, osqlstr, strlen(osqlstr), &oins, NULL);
    o.id = 5;
    strcpy(o.customer, "E E");
    o.nlines = 1;
    o.line[0].id = 500;
    o.line[0].orderid = 5;
    strcpy(o.line[0].item, "A good C book");
    o.line[0].qty = 1;
    for(i = 0; i < o.nlines; i++)
    {
        stat = sqlite3_prepare(con, olsqlstr, strlen(olsqlstr), &olins, NULL);
        sqlite3_bind_int(olins, 1, o.line[i].id);
        sqlite3_bind_int(olins, 2, o.line[i].orderid);
        sqlite3_bind_text(olins, 3, o.line[0].item, strlen(o.line[i].item), NULL);
        sqlite3_bind_int(olins, 4, o.line[i].qty);
        stat = sqlite3_step(olins);
        sqlite3_finalize(olins);
    }
    sqlite3_bind_int(oins, 1, o.id);
    sqlite3_bind_text(oins, 2, o.customer, strlen(o.customer), NULL);
    stat = sqlite3_step(oins);
    sqlite3_finalize(oins);
    stat = sqlite3_prepare(con, osqlstr, strlen(osqlstr), &oins, NULL);
    o.id = 6;
    strcpy(o.customer, "F F");
    o.nlines = 1;
    o.line[0].id = 600;
    o.line[0].orderid = 6;
    strcpy(o.line[0].item, "Another good C book");
    o.line[0].qty = 1;
    for(i = 0; i < o.nlines; i++)
    {
        stat = sqlite3_prepare(con, olsqlstr, strlen(olsqlstr), &olins, NULL);
        sqlite3_bind_int(olins, 1, o.line[i].id);
        sqlite3_bind_int(olins, 2, o.line[i].orderid);
        sqlite3_bind_text(olins, 3, o.line[0].item, strlen(o.line[i].item), NULL);
        sqlite3_bind_int(olins, 4, o.line[i].qty);
        stat = sqlite3_step(olins);
        sqlite3_finalize(olins);
    }
    sqlite3_bind_int(oins, 1, o.id);
    sqlite3_bind_text(oins, 2, o.customer, strlen(o.customer), NULL);
    stat = sqlite3_step(oins);
    sqlite3_finalize(oins);
    sqlite3_close(con);
    return 0;
}

Build and run:

$ cc/include=sqlite3_include: insert_after_1
$ link insert_after_1 + sys$input/opt
sqlite3shr/share
$
$ run insert_after_1

order.pas:

type
   orderline = record
                  id : integer;
                  orderid : integer;
                  item : varying[32] of char;
                  qty : unsigned;
               end;
   order = record
              id : unsigned;
              customer : varying[32] of char;
              nlines : integer;
              line : array [1..100] of orderline;
           end;
[inherit('psqlitedir:sqlite', 'psqlitedir:psqlite')]
program insert_after_2(input, output);

%include 'order.pas'

var
   con : sqlite_ptr;
   oins, olins : sqlite_stmt_ptr;
   o : order;
   i : integer;

begin
   con := psqlite_open('order.db');
   oins := psqlite_prepare(con, 'INSERT INTO myorder VALUES(?,?)');
   o.id := 7;
   o.customer := 'G G';
   o.nlines := 1;
   o.line[1].id := 700;
   o.line[1].orderid := 7;
   o.line[1].item := 'A good Pascal book';
   o.line[1].qty := 1;
   for i := 1 to o.nlines do begin
      olins := psqlite_prepare(con, 'INSERT INTO orderline VALUES(?,?,?,?)');
      psqlite_bind_int(olins, 1, o.line[i].id);
      psqlite_bind_int(olins, 2, o.line[i].orderid);
      psqlite_bind_text(olins, 3, o.line[i].item);
      psqlite_bind_int(olins, 4, o.line[i].qty);
      psqlite_step_nonquery(olins);
      psqlite_finalize(olins);
   end;
   psqlite_bind_int(oins, 1, o.id);
   psqlite_bind_text(oins, 2, o.customer);
   psqlite_step_nonquery(oins);
   psqlite_finalize(oins);
   oins := psqlite_prepare(con, 'INSERT INTO myorder VALUES(?,?)');
   o.id := 8;
   o.customer := 'H H';
   o.nlines := 1;
   o.line[1].id := 800;
   o.line[1].orderid := 8;
   o.line[1].item := 'Another good Pascal book';
   o.line[1].qty := 1;
   for i := 1 to o.nlines do begin
      olins := psqlite_prepare(con, 'INSERT INTO orderline VALUES(?,?,?,?)');
      psqlite_bind_int(olins, 1, o.line[i].id);
      psqlite_bind_int(olins, 2, o.line[i].orderid);
      psqlite_bind_text(olins, 3, o.line[i].item);
      psqlite_bind_int(olins, 4, o.line[i].qty);
      psqlite_step_nonquery(olins);
      psqlite_finalize(olins);
   end;
   psqlite_bind_int(oins, 1, o.id);
   psqlite_bind_text(oins, 2, o.customer);
   psqlite_step_nonquery(oins);
   psqlite_finalize(oins);
   psqlite_close(con);
end.

Build and run:

$ pas insert_after_2
$ link insert_after_2 + sys$input/opt + psqlitedir:sqlite/opt
psqlitedir:psqlite
psqlitedir:sqlite
$
$ run insert_after_2

order.py:

class OrderLine(object):
    def __init__(self, _id = 0, _orderid = 0, _item = '', _qty = 0):
        self.id = _id
        self.orderid = _orderid
        self.item = _item
        self.qty = _qty

class Order(object):
    def __init__(self, _id = 0, _customer = '', _lines = []):
        self.id = _id
        self.customer = _customer
        self.lines = _lines
import sqlite3

from order import Order, OrderLine

con = sqlite3.connect('order.db')
c = con.cursor()
o = Order(9, 'I I', [ OrderLine(900, 9, 'A good Python book', 1) ]);
for ol in o.lines:
    c.execute('INSERT INTO orderline VALUES(?,?,?,?)', (ol.id, ol.orderid, ol.item, ol.qty))
c.execute('INSERT INTO myorder VALUES(?,?)', (o.id, o.customer))
o = Order(10, 'J J', [ OrderLine(1000, 10, 'Another good Python book', 1) ]);
for ol in o.lines:
    c.execute('INSERT INTO orderline VALUES(?,?,?,?)', (ol.id, ol.orderid, ol.item, ol.qty))
c.execute('INSERT INTO myorder VALUES(?,?)', (o.id, o.customer))
con.commit()
con.close()

order.php:

<?php
class OrderLine {
    public $id;
    public $orderid;
    public $item;
    public $qty;
    public function __construct($id = 0, $orderid = 0, $item = '', $qty = 0) {
        $this->id = $id;
        $this->orderid = $orderid;
        $this->item = $item;
        $this->qty = $qty;
    }
}

class Order {
    public $id;
    public $customer;
    public $lines;
    public function __construct($id = 0, $customer = '', $lines = array()) {
        $this->id = $id;
        $this->customer = $customer;
        $this->lines = $lines;
    }
}

?>
<?php
include '/disk2/arne/migr/reltwo/order.php';

$con = new PDO('sqlite:/disk2/arne/migr/reltwo/order.db');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$ostmt = $con->prepare('INSERT INTO myorder VALUES(:ID,:CUSTOMER)');
$olstmt = $con->prepare('INSERT INTO orderline VALUES(:ID,:ORDERID,:ITEM,:QTY)');
$o = new Order(11, 'K K', array(new OrderLine(1100, 11, 'A good PHP book', 1)));
foreach($o->lines as $ol) {
    $olstmt->execute(array(':ID' => $ol->id, ':ORDERID' => $ol->orderid, ':ITEM' => $ol->item, ':QTY' => $ol->qty));
}
$ostmt->execute(array(':ID' => $o->id, ':CUSTOMER' => $o->customer));
$o = new Order(12, 'L L', array(new OrderLine(1200, 12, 'Another good PHP book', 1)));
foreach($o->lines as $ol) {
    $olstmt->execute(array(':ID' => $ol->id, ':ORDERID' => $ol->orderid, ':ITEM' => $ol->item, ':QTY' => $ol->qty));
}
$ostmt->execute(array(':ID' => $o->id, ':CUSTOMER' => $o->customer));
?>
public class OrderLine {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLine() {
        this(0, 0, "", 0);
    }
    public OrderLine(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

public class Order {
    private int id;
    private String customer;
    private List<OrderLine> lines;
    public Order() {
        this(0, "", new ArrayList<OrderLine>());
    }
    public Order(int id, String customer) {
        this(id, customer, new ArrayList<OrderLine>());
    }
    public Order(int id, String customer, List<OrderLine> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    public List<OrderLine> getLines() {
        return lines;
    }
    public void setLines(List<OrderLine> lines) {
        this.lines = lines;
    }
}
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;

public class InsertAfter5 {
    public static void main(String[] args) throws Exception {
        Class.forName("org.sqlite.JDBC");
        Connection con = DriverManager.getConnection("jdbc:sqlite:order.db");
        PreparedStatement opstmt = con.prepareStatement("INSERT INTO myorder VALUES(?,?)");
        PreparedStatement olpstmt = con.prepareStatement("INSERT INTO orderline VALUES(?,?,?,?)");
        Order o;
        o = new Order(13, "M M");
        o.getLines().add(new OrderLine(1300, 13, "A good Java book", 1));
        for(OrderLine ol : o.getLines()) {
            olpstmt.setInt(1, ol.getId());
            olpstmt.setInt(2, ol.getOrderId());
            olpstmt.setString(3, ol.getItem());
            olpstmt.setInt(4, ol.getQty());
            olpstmt.executeUpdate();
        }
        opstmt.setInt(1, o.getId());
        opstmt.setString(2, o.getCustomer());
        opstmt.executeUpdate();
        o = new Order(14, "N N");
        o.getLines().add(new OrderLine(1400, 14, "Another good Java book", 1));
        for(OrderLine ol : o.getLines()) {
            olpstmt.setInt(1, ol.getId());
            olpstmt.setInt(2, ol.getOrderId());
            olpstmt.setString(3, ol.getItem());
            olpstmt.setInt(4, ol.getQty());
            olpstmt.executeUpdate();
        }
        opstmt.setInt(1, o.getId());
        opstmt.setString(2, o.getCustomer());
        opstmt.executeUpdate();
        opstmt.close();
        con.close();
    }
}

Build and run:

$ javac InsertAfter5.java Order.java OrderLine.java
$ java -cp .:/javalib/sqlite-jdbc-3_14_1-vms.jar "InsertAfter5"
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="orderline")
public class OrderLineJPA {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLineJPA() {
        this(0, 0, "", 0);
    }
    public OrderLineJPA(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="orderid")
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    @Column(name="item")
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    @Column(name="qty")
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="myorder")
public class OrderJPA {
    private int id;
    private String customer;
    private List<OrderLineJPA> lines;
    public OrderJPA() {
        this(0, "", new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer) {
        this(id, customer, new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer, List<OrderLineJPA> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="customer")
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    @OneToMany(mappedBy = "orderId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    public List<OrderLineJPA> getLines() {
        return lines;
    }
    public void setLines(List<OrderLineJPA> lines) {
        this.lines = lines;
    }
}
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;

public class InsertAfter6 {
    public static void main(String[] args) throws Exception {
        Logger.getLogger("org.hibernate").setLevel(Level.OFF);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Order");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        OrderJPA o;
        o = new OrderJPA(15, "O O");
        o.getLines().add(new OrderLineJPA(1500, 15, "A good Java book", 1));
        em.persist(o);
        o = new OrderJPA(16, "P P");
        o.getLines().add(new OrderLineJPA(1600, 16, "Another good Java book", 1));
        em.persist(o);
        em.getTransaction().commit();
        em.close();
        emf.close();
    }
}

Build and run:

$ javac -cp 'hibpath' InsertAfter6.java OrderJPA.java OrderLineJPA.java
$ java -cp .:'hibpath':/javalib/sqlite-jdbc-3_14_1-vms.jar:/javalib/sqlitehib.jar "InsertAfter6"

List:

order.h:

struct orderline
{
    int id;
    int orderid;
    char item[33];
    int qty;
};

struct order
{
    int id;
    char customer[33];
    int nlines;
    struct orderline line[100];    
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "sqlite3.h"

#include "order.h"

int main()
{
    sqlite3 *con;
    sqlite3_stmt *sel;
    char *sqlstr = "SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid";
    int stat;
    int lastid;
    int count;
    struct order res[1000];
    int i, j;
    stat = sqlite3_open("order.db", &con);
    stat = sqlite3_prepare(con, sqlstr, strlen(sqlstr), &sel, NULL);
    lastid = -1;
    count = 0;
    while(sqlite3_step(sel) == SQLITE_ROW)
    {
        if(sqlite3_column_int(sel, 0) != lastid)
        {
            lastid = sqlite3_column_int(sel, 0);
            count++;
            res[count-1].id = sqlite3_column_int(sel, 0);
            strcpy(res[count-1].customer, (char *)sqlite3_column_text(sel, 1));
            res[count-1].nlines = 0;
        }
        res[count-1].line[res[count-1].nlines].id = sqlite3_column_int(sel, 2);
        res[count-1].line[res[count-1].nlines].orderid = sqlite3_column_int(sel, 3);
        strcpy(res[count-1].line[res[count-1].nlines].item, (char *)sqlite3_column_text(sel, 4));
        res[count-1].line[res[count-1].nlines].qty = sqlite3_column_int(sel, 5);
        res[count-1].nlines++;
    }
    sqlite3_finalize(sel);
    sqlite3_close(con);
    for(i = 0; i < count; i++)
    {
        printf("%d %s\n", res[i].id, res[i].customer);
        for(j = 0; j < res[i].nlines; j++)
        {
            printf("  %s : %d\n", res[i].line[j].item, res[i].line[j].qty);
        }
    }
    return 0;
}

Build and run:

$ cc/include=sqlite3_include: list_after_1
$ link list_after_1 + sys$input/opt
sqlite3shr/share
$
$ run list_after_1

order.pas:

type
   orderline = record
                  id : integer;
                  orderid : integer;
                  item : varying[32] of char;
                  qty : unsigned;
               end;
   order = record
              id : unsigned;
              customer : varying[32] of char;
              nlines : integer;
              line : array [1..100] of orderline;
           end;
[inherit('psqlitedir:sqlite', 'psqlitedir:psqlite')]
program list_after_2(input, output);

%include 'order.pas'

var
   con : sqlite_ptr;
   sel : sqlite_stmt_ptr;
   res : array [1..1000] of order;
   count : integer;
   lastid : integer;
   i, j : integer;

begin
   con := psqlite_open('order.db');
   sel := psqlite_prepare(con, 'SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid');
   count := 0;
   lastid := -1;
   while psqlite_step_query(sel) do begin
      if psqlite_column_int(sel, 0) <> lastid then begin
         lastid := psqlite_column_int(sel, 0);
         count := count + 1;
         res[count].id := psqlite_column_int(sel, 0);
         res[count].customer := psqlite_column_text(sel, 1);
         res[count].nlines := 0;
      end; 
      res[count].nlines := res[count].nlines + 1;
      res[count].line[res[count].nlines].id := psqlite_column_int(sel, 2);
      res[count].line[res[count].nlines].orderid := psqlite_column_int(sel, 3);
      res[count].line[res[count].nlines].item := psqlite_column_text(sel, 4);
      res[count].line[res[count].nlines].qty := psqlite_column_int(sel, 5);
   end;
   psqlite_finalize(sel);
   psqlite_close(con);
   for i := 1 to count do begin
      writeln(res[i].id:1, ' ', res[i].customer);
      for j := 1 to res[i].nlines do begin
         writeln('  ', res[i].line[j].item, ': ', res[i].line[j].qty);
      end;
   end;
end.

Build and run:

$ pas list_after_2
$ link list_after_2 + sys$input/opt + psqlitedir:sqlite/opt
psqlitedir:psqlite
psqlitedir:sqlite
$
$ run list_after_2

order.py:

class OrderLine(object):
    def __init__(self, _id = 0, _orderid = 0, _item = '', _qty = 0):
        self.id = _id
        self.orderid = _orderid
        self.item = _item
        self.qty = _qty

class Order(object):
    def __init__(self, _id = 0, _customer = '', _lines = []):
        self.id = _id
        self.customer = _customer
        self.lines = _lines
import sqlite3

from order import Order, OrderLine

con = sqlite3.connect('order.db')
c = con.cursor()
c.execute('SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid')
res = []
lastid = -1
lasto = None
for row in c.fetchall():
    if row[0] != lastid:
        lastid = row[0]
        lasto = Order(row[0], row[1], [])
        res.append(lasto)
    lasto.lines.append(OrderLine(row[2], row[3], row[4], row[5]))
con.commit()
con.close()

for o in res:
    print('%d %s' % (o.id, o.customer))
    for ol in o.lines:
        print('  %s : %d' % (ol.item, ol.qty))

order.php:

<?php
class OrderLine {
    public $id;
    public $orderid;
    public $item;
    public $qty;
    public function __construct($id = 0, $orderid = 0, $item = '', $qty = 0) {
        $this->id = $id;
        $this->orderid = $orderid;
        $this->item = $item;
        $this->qty = $qty;
    }
}

class Order {
    public $id;
    public $customer;
    public $lines;
    public function __construct($id = 0, $customer = '', $lines = array()) {
        $this->id = $id;
        $this->customer = $customer;
        $this->lines = $lines;
    }
}

?>
<?php
include '/disk2/arne/migr/reltwo/order.php';

$con = new PDO('sqlite:/disk2/arne/migr/reltwo/order.db');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_NUM);
$stmt = $con->prepare('SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid');
$stmt->execute(array());
$res = array();
$lastid = -1;
$lasto = null;
while($row = $stmt->fetch()) {
    if($lastid != $row[0]) {
        $lastid = $row[0];
        $lasto = new Order($row[0], $row[1], array());
        $res[] = $lasto;
    }
    $lasto->lines[] = new OrderLine($row[2], $row[3], $row[4], $row[5]);
}

foreach($res as $o) {
    echo sprintf('%d %s', $o->id, $o->customer) . "\r\n";
    foreach($o->lines as $ol) {
        echo sprintf('  %s : %d', $ol->item, $ol->qty) . "\r\n";
    }
}
?>
public class OrderLine {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLine() {
        this(0, 0, "", 0);
    }
    public OrderLine(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

public class Order {
    private int id;
    private String customer;
    private List<OrderLine> lines;
    public Order() {
        this(0, "", new ArrayList<OrderLine>());
    }
    public Order(int id, String customer) {
        this(id, customer, new ArrayList<OrderLine>());
    }
    public Order(int id, String customer, List<OrderLine> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    public List<OrderLine> getLines() {
        return lines;
    }
    public void setLines(List<OrderLine> lines) {
        this.lines = lines;
    }
}
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class ListAfter5 {
    public static void main(String[] args) throws Exception {
        Class.forName("org.sqlite.JDBC");
        List<Order> res = new ArrayList<Order>();
        Connection con = DriverManager.getConnection("jdbc:sqlite:order.db");
        Statement stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT myorder.id,myorder.customer,orderline.id,orderline.orderid,orderline.item,orderline.qty FROM myorder JOIN orderline ON myorder.id = orderline.orderid");
        int lastid = -1;
        Order lasto = null;
        while(rs.next()) {
            int id = rs.getInt(1);
            String customer = rs.getString(2);
            int lineid = rs.getInt(3);
            int orderid = rs.getInt(4);
            String item = rs.getString(5);
            int qty = rs.getInt(6);
            if(id != lastid) {
                lastid = id;
                lasto = new Order(id, customer);
                res.add(lasto);
            }
            OrderLine line = new OrderLine(lineid, orderid, item, qty);
            lasto.getLines().add(line);
        }
        rs.close();
        stmt.close();
        con.close();
        for(Order o : res) {
            System.out.printf("%d %s\n", o.getId(), o.getCustomer());
            for(OrderLine ol : o.getLines()) {
                System.out.printf("  %s : %d\n", ol.getItem(), ol.getQty());
            }
        }
    }
}

Build and run:

$ javac ListAfter5.java Order.java OrderLine.java
$ java -cp .:/javalib/sqlite-jdbc-3_14_1-vms.jar "ListAfter5"
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="orderline")
public class OrderLineJPA {
    private int id;
    private int orderId;
    private String item;
    private int qty;
    public OrderLineJPA() {
        this(0, 0, "", 0);
    }
    public OrderLineJPA(int id, int orderId, String item, int qty) {
        this.id = id;
        this.orderId = orderId;
        this.item = item;
        this.qty = qty;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="orderid")
    public int getOrderId() {
        return orderId;
    }
    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }
    @Column(name="item")
    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    @Column(name="qty")
    public int getQty() {
        return qty;
    }
    public void setQty(int qty) {
        this.qty = qty;
    }
}
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="myorder")
public class OrderJPA {
    private int id;
    private String customer;
    private List<OrderLineJPA> lines;
    public OrderJPA() {
        this(0, "", new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer) {
        this(id, customer, new ArrayList<OrderLineJPA>());
    }
    public OrderJPA(int id, String customer, List<OrderLineJPA> lines) {
        this.id = id;
        this.customer = customer;
        this.lines = lines;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="customer")
    public String getCustomer() {
        return customer;
    }
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    @OneToMany(mappedBy = "orderId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    public List<OrderLineJPA> getLines() {
        return lines;
    }
    public void setLines(List<OrderLineJPA> lines) {
        this.lines = lines;
    }
}
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;

public class ListAfter6 {
    public static void main(String[] args) throws Exception {
        Logger.getLogger("org.hibernate").setLevel(Level.OFF);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Order");
        EntityManager em = emf.createEntityManager();
        TypedQuery<OrderJPA> q = em.createQuery("SELECT o FROM OrderJPA AS o JOIN FETCH o.lines ol", OrderJPA.class);
        List<OrderJPA> res = q.getResultList();
        for(OrderJPA o : res) {
            System.out.printf("%d %s\n", o.getId(), o.getCustomer());
            for(OrderLineJPA ol : o.getLines()) {
                System.out.printf("  %s : %d\n", ol.getItem(), ol.getQty());
            }
        }
        em.close();
        emf.close();
    }
}

Build and run:

$ javac -cp 'hibpath' ListAfter6.java OrderJPA.java OrderLineJPA.java
$ java -cp .:'hibpath':/javalib/sqlite-jdbc-3_14_1-vms.jar:/javalib/sqlitehib.jar "ListAfter6"

Inheritance/variant example:

Data model:

Before records:

id description assettype model license towcapacity
1 Red Truck TRUCK GMC Sierra AB1234 6000
id description assettype ownweight maxcargo roof
2 Small trailer TRAILER 500 3000 REALROOF
id description assettype tooltype name weight
3 Lawn tractor #1 TOOL Lawn tractor Craftsman 1000

After database tables:

asset

id description assettype
1 Red truck TRUCK
2 Small trailer TRAILER
3 Lawn tractor #1 TOOL

truckasset

id model license towcapacity
1 GMC Sierrra AB1234 6000

trailerasset

id ownweight maxcargo roof
2 500 3000 REALROOF

toolasset

id tooltype name weight
3 Lawn tractor Craftsman 1000

Before:

Insert:

IDENTIFICATION DIVISION.
PROGRAM-ID.INSERT-BEFORE-1.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT OPTIONAL ASSET-FILE ASSIGN TO "asset.isq" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS ASSET-ID.

DATA DIVISION.
FILE SECTION.
FD ASSET-FILE.
01 ASSET-RECORD.
    03 ASSET-ID PIC 9(8) COMP.
    03 ASSET-DESCRIPTION PIC X(32).
    03 ASSET-ASSETTYPE PIC 9(8) COMP.
    03 TRUCK-ASSET.
       05 TRUCK-MODEL PIC X(16).
       05 TRUCK-LICENSE PIC X(8).
       05 TRUCK-TOWCAPACITY PIC 9(8) COMP.
       05 FILLER PIC X(8).
    03 TRAILER-ASSET REDEFINES TRUCK-ASSET.
       05 TRAILER-OWNWEIGHT PIC 9(8) COMP.
       05 TRAILER-MAXCARGO PIC 9(8) COMP.
       05 TRAILER-ROOF PIC 9(8) COMP.
    03 TOOL-ASSET REDEFINES TRUCK-ASSET.
       05 TOOL-TOOLTYPE PIC X(16).
       05 TOOL-NAME PIC X(16).
       05 TOOL-WEIGHT PIC 9(8) COMP.
WORKING-STORAGE SECTION.
01 TRUCK PIC 9(8) COMP VALUE 1.
01 TRAILER PIC 9(8) COMP VALUE 2.
01 TOOL PIC 9(8) COMP VALUE 3.
01 PERMOPEN PIC 9(8) COMP VALUE 1.
01 COVERABLE PIC 9(8) COMP VALUE 2.
01 REALROOF PIC 9(8) COMP VALUE 3.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
    OPEN I-O ASSET-FILE
    MOVE 1 TO ASSET-ID
    MOVE "Red truck" TO ASSET-DESCRIPTION
    MOVE TRUCK TO ASSET-ASSETTYPE
    MOVE "GMC Sierra" TO TRUCK-MODEL
    MOVE "AB1234" TO TRUCK-LICENSE
    MOVE 6000 TO TRUCK-TOWCAPACITY
    PERFORM INSERT-PARAGRAPH
    MOVE 2 TO ASSET-ID
    MOVE "Small trailer" TO ASSET-DESCRIPTION
    MOVE TRAILER TO ASSET-ASSETTYPE
    MOVE 500 TO TRAILER-OWNWEIGHT
    MOVE 3000 TO TRAILER-MAXCARGO
    MOVE REALROOF TO TRAILER-ROOF
    PERFORM INSERT-PARAGRAPH
    MOVE 3 TO ASSET-ID
    MOVE "Lawn tracctor #1" TO ASSET-DESCRIPTION
    MOVE TOOL TO ASSET-ASSETTYPE
    MOVE "Lawn tractor" TO TOOL-TOOLTYPE
    MOVE "Craftsman" TO TOOL-NAME
    MOVE 1000 TO TOOL-WEIGHT
    PERFORM INSERT-PARAGRAPH
    CLOSE ASSET-FILE
    STOP RUN.
INSERT-PARAGRAPH.
    WRITE ASSET-RECORD
        INVALID KEY DISPLAY "Error writing"
        NOT INVALID KEY CONTINUE
    END-WRITE.

Build and run:

$ cob insert_before_1
$ link insert_before_1
$ run insert_before_1

asset_isam.pas:

const
   TRUCK = 1;
   TRAILER = 2;
   TOOL = 3;

const
   PERMOPEN = 1;
   COVERABLE = 2;
   REALROOF = 3;

type
   asset = record
               id : [key(0)]unsigned;
               description : packed array [1..32] of char;
               assettype : integer;
               case integer of
                  TRUCK:
                     (
                        model : packed array[1..16] of char;
                        license : packed array[1..8] of char;
                        towcapacity : integer;
                     );
                  TRAILER:
                     (
                        ownweight : integer;
                        maxcargo : integer;
                        roof : integer;
                     );
                  TOOL:
                     (
                        tooltype : packed array[1..16] of char;
                        name : packed array[1..16] of char;
                        weight : integer;
                     );
            end;
program insert_before_2(input, output);

%include 'asset_isam.pas'

var
   a : asset;
   db : file of asset;

begin
   open(db, 'asset.isq', unknown, organization := indexed, access_method := keyed);
   a.id := 4;
   a.description := 'Blue truck';
   a.assettype := TRUCK;
   a.model := 'Ford F-250';
   a.license := 'CD5678';
   a.towcapacity := 10000;
   db^ := a;
   put(db);
   a.id := 5;
   a.description := 'Large trailer';
   a.assettype := TRAILER;
   a.ownweight := 1000;
   a.maxcargo := 6000;
   a.roof := PERMOPEN;
   db^ := a;
   put(db);
   a.id := 6;
   a.description := 'Lawn tractor #2';
   a.assettype := TOOL;
   a.tooltype := 'Lawn tractor';
   a.name := 'Craftsman';
   a.weight := 1000;
   db^ := a;
   put(db);
   close(db);
end.

Build and run:

$ pas insert_before_2
$ link insert_before_2
$ run insert_before_2

List:

IDENTIFICATION DIVISION.
PROGRAM-ID.LIST-BEFORE-1.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT OPTIONAL ASSET-FILE ASSIGN TO "asset.isq" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS ASSET-ID.

DATA DIVISION.
FILE SECTION.
FD ASSET-FILE.
01 ASSET-RECORD.
    03 ASSET-ID PIC 9(8) COMP.
    03 ASSET-DESCRIPTION PIC X(32).
    03 ASSET-ASSETTYPE PIC 9(8) COMP.
    03 TRUCK-ASSET.
       05 TRUCK-MODEL PIC X(16).
       05 TRUCK-LICENSE PIC X(8).
       05 TRUCK-TOWCAPACITY PIC 9(8) COMP.
       05 FILLER PIC X(8).
    03 TRAILER-ASSET REDEFINES TRUCK-ASSET.
       05 TRAILER-OWNWEIGHT PIC 9(8) COMP.
       05 TRAILER-MAXCARGO PIC 9(8) COMP.
       05 TRAILER-ROOF PIC 9(8) COMP.
    03 TOOL-ASSET REDEFINES TRUCK-ASSET.
       05 TOOL-TOOLTYPE PIC X(16).
       05 TOOL-NAME PIC X(16).
       05 TOOL-WEIGHT PIC 9(8) COMP.
WORKING-STORAGE SECTION.
01 TRUCK PIC 9(8) COMP VALUE 1.
01 TRAILER PIC 9(8) COMP VALUE 2.
01 TOOL PIC 9(8) COMP VALUE 3.
01 PERMOPEN PIC 9(8) COMP VALUE 1.
01 COVERABLE PIC 9(8) COMP VALUE 2.
01 REALROOF PIC 9(8) COMP VALUE 3.
01 EOF-FLAG PIC X.         
01 ID PIC 9(8) DISPLAY.
01 TOWCAPACITY PIC 9(8) DISPLAY.
01 OWNWEIGHT PIC 9(8) DISPLAY.
01 MAXCARGO PIC 9(8) DISPLAY.
01 ROOF PIC 9(8) DISPLAY.
01 WEIGHT PIC 9(8) DISPLAY.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
    OPEN I-O ASSET-FILE
    MOVE 'N' TO EOF-FLAG
    PERFORM UNTIL EOF-FLAG = 'Y'
        READ ASSET-FILE NEXT
            AT END MOVE 'Y' TO EOF-FLAG
            NOT AT END PERFORM SHOW-PARAGRAPH
        END-READ
    END-PERFORM
    CLOSE ASSET-FILE
    STOP RUN.
SHOW-PARAGRAPH.
    EVALUATE ASSET-ASSETTYPE
        WHEN TRUCK PERFORM SHOW-TRUCK-PARAGRAPH
        WHEN TRAILER PERFORM SHOW-TRAILER-PARAGRAPH
        WHEN TOOL PERFORM SHOW-TOOL-PARAGRAPH.
SHOW-TRUCK-PARAGRAPH.
    MOVE ASSET-ID TO ID
    MOVE TRUCK-TOWCAPACITY TO TOWCAPACITY
    DISPLAY ID " " ASSET-DESCRIPTION " " TRUCK-MODEL " " TRUCK-LICENSE " " TOWCAPACITY.
SHOW-TRAILER-PARAGRAPH.
    MOVE ASSET-ID TO ID
    MOVE TRAILER-OWNWEIGHT TO OWNWEIGHT
    MOVE TRAILER-MAXCARGO TO MAXCARGO
    MOVE TRAILER-ROOF TO ROOF
    DISPLAY ID " " ASSET-DESCRIPTION " " OWNWEIGHT " " MAXCARGO " " ROOF.
SHOW-TOOL-PARAGRAPH.
    MOVE ASSET-ID TO ID
    MOVE TOOL-WEIGHT TO WEIGHT
    DISPLAY ID " " ASSET-DESCRIPTION " " TOOL-TOOLTYPE " " TOOL-NAME " " WEIGHT.

Build and run:

$ cob list_before_1
$ link list_before_1
$ run list_before_1

asset_isam.pas:

const
   TRUCK = 1;
   TRAILER = 2;
   TOOL = 3;

const
   PERMOPEN = 1;
   COVERABLE = 2;
   REALROOF = 3;

type
   asset = record
               id : [key(0)]unsigned;
               description : packed array [1..32] of char;
               assettype : integer;
               case integer of
                  TRUCK:
                     (
                        model : packed array[1..16] of char;
                        license : packed array[1..8] of char;
                        towcapacity : integer;
                     );
                  TRAILER:
                     (
                        ownweight : integer;
                        maxcargo : integer;
                        roof : integer;
                     );
                  TOOL:
                     (
                        tooltype : packed array[1..16] of char;
                        name : packed array[1..16] of char;
                        weight : integer;
                     );
            end;
program list_before_2(input, output);

%include 'asset_isam.pas'

var
   a : asset;
   db : file of asset;

begin
   open(db, 'asset.isq', unknown, organization := indexed, access_method := keyed);
   resetk(db, 0);
   while not eof(db) do begin
      a := db^;
      write(a.id, ' ', a.description, ' ');
      case a.assettype of
         TRUCK:
            begin
               write(a.model, ' ', a.license, ' ', a.towcapacity);
            end;
         TRAILER:
            begin
               write(a.ownweight, ' ', a.maxcargo, ' ', a.roof);
            end;
         TOOL:
            begin
               write(a.tooltype, ' ', a.name, ' ', a.weight);
            end;
      end;
      writeln;
      get(db);
   end;
   close(db);
end.

Build and run:

$ pas list_before_2
$ link list_before_2
$ run list_before_2

Convert:

The variants within a record require a new feature in the annotations:

@Selector
to indicate what sub-class to instantiate for a given value of the variant selector field (see example below)
import dk.vajhoej.isam.KeyField;
import dk.vajhoej.record.Alignment;
import dk.vajhoej.record.Endian;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Selector;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;
import dk.vajhoej.record.SubType;

@Struct(endianess=Endian.LITTLE, alignment=Alignment.ALIGN1)
public class AssetISAM {
    @KeyField(n=0)
    @StructField(n=0, type=FieldType.INT4)
    private int id;
    @StructField(n=1, type=FieldType.FIXSTR, length=32)
    private String description;
    @StructField(n=2, type=FieldType.INT4)
    @Selector(subtypes={@SubType(value=1,type=TruckAssetISAM.class),
                        @SubType(value=2,type=TrailerAssetISAM.class),
                        @SubType(value=3,type=ToolAssetISAM.class)})
    private int assetType;
    public AssetISAM() {
        this(0, "", 0);
    }
    public AssetISAM(int id, String description, int assetType) {
        this.id = id;
        this.description = description;
        this.assetType = assetType;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public int getAssetType() {
        return assetType;
    }
    public void setAssetType(int assetType) {
        this.assetType = assetType;
    }
}
import dk.vajhoej.record.Alignment;
import dk.vajhoej.record.Endian;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct(endianess=Endian.LITTLE, alignment=Alignment.ALIGN1)
public class TruckAssetISAM extends AssetISAM {
    @StructField(n=3, type=FieldType.FIXSTR, length=16)
    private String model;
    @StructField(n=4, type=FieldType.FIXSTR, length=8)
    private String license;
    @StructField(n=5, type=FieldType.INT4)
    private int towcapacity;
    public TruckAssetISAM() {
        this(0, "", "", "", 0);
    }
    public TruckAssetISAM(int id, String description, String model, String license, int towcapacity) {
        super(id, description, 1);
        this.model = model;
        this.license = license;
        this.towcapacity = towcapacity;
    }
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    public String getLicense() {
        return license;
    }
    public void setLicense(String license) {
        this.license = license;
    }
    public int getTowcapacity() {
        return towcapacity;
    }
    public void setTowcapacity(int towcapacity) {
        this.towcapacity = towcapacity;
    }
}
import dk.vajhoej.record.Alignment;
import dk.vajhoej.record.Endian;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct(endianess=Endian.LITTLE, alignment=Alignment.ALIGN1)
public class TrailerAssetISAM extends AssetISAM {
    @StructField(n=3, type=FieldType.INT4)
    private int ownWeight;
    @StructField(n=4, type=FieldType.INT4)
    private int maxCargo;
    @StructField(n=5, type=FieldType.INT4)
    private int roof;
    public TrailerAssetISAM() {
        this(0, "", 0, 0, 0);
    }
    public TrailerAssetISAM(int id, String description, int ownWeight, int maxCargo, int roof) {
        super(id, description, 2);
        this.ownWeight = ownWeight;
        this.maxCargo = maxCargo;
        this.roof = roof;
    }
    public int getOwnWeight() {
        return ownWeight;
    }
    public void setOwnWeight(int ownWeight) {
        this.ownWeight = ownWeight;
    }
    public int getMaxCargo() {
        return maxCargo;
    }
    public void setMaxCargo(int maxCargo) {
        this.maxCargo = maxCargo;
    }
    public int getRoof() {
        return roof;
    }
    public void setRoof(int roof) {
        this.roof = roof;
    }
}
import dk.vajhoej.record.Alignment;
import dk.vajhoej.record.Endian;
import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct(endianess=Endian.LITTLE, alignment=Alignment.ALIGN1)
public class ToolAssetISAM extends AssetISAM {
    @StructField(n=3, type=FieldType.FIXSTR, length=16)
    private String toolType;
    @StructField(n=4, type=FieldType.FIXSTR, length=16)
    private String name;
    @StructField(n=5, type=FieldType.INT4)
    private int weight;
    public ToolAssetISAM() {
        this(0, "", "", "", 0);
    }
    public ToolAssetISAM(int id, String description, String toolType, String name, int weight) {
        super(id, description, 3);
        this.toolType = toolType;
        this.name = name;
        this.weight = weight;
    }
    public String getToolType() {
        return toolType;
    }
    public void setToolType(String toolType) {
        this.toolType = toolType;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}

The inheritance requires two new features in the annotations:

@Inheritance
to indicate that we want to implement inheritance using join
@PrimaryKeyJoinColumn
to indicate what column to join on
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="asset")
@Inheritance(strategy=InheritanceType.JOINED)
public class AssetJPA {
    public static final int TRUCK = 1;
    public static final int TRAILER = 2;
    public static final int TOOL = 3;
    private int id;
    private String description;
    private int assetType;
    public AssetJPA() {
        this(0, "", 0);
    }
    public AssetJPA(int id, String description, int assetType) {
        this.id = id;
        this.description = description;
        this.assetType = assetType;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="description")
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    @Column(name="assettype")
    public int getAssetType() {
        return assetType;
    }
    public void setAssetType(int assetType) {
        this.assetType = assetType;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="truckasset")
@PrimaryKeyJoinColumn(name="id")
public class TruckAssetJPA extends AssetJPA {
    private String model;
    private String license;
    private int towcapacity;
    public TruckAssetJPA() {
        this(0, "", "", "", 0);
    }
    public TruckAssetJPA(int id, String description, String model, String license, int towcapacity) {
        super(id, description, TRUCK);
        this.model = model;
        this.license = license;
        this.towcapacity = towcapacity;
    }
    @Column(name="model")
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    @Column(name="license")
    public String getLicense() {
        return license;
    }
    public void setLicense(String license) {
        this.license = license;
    }
    @Column(name="towcapacity")
    public int getTowcapacity() {
        return towcapacity;
    }
    public void setTowcapacity(int towcapacity) {
        this.towcapacity = towcapacity;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="trailerasset")
@PrimaryKeyJoinColumn(name="id")
public class TrailerAssetJPA extends AssetJPA {
    private int ownWeight;
    private int maxCargo;
    private int roof;
    public TrailerAssetJPA() {
        this(0, "", 0, 0, 0);
    }
    public TrailerAssetJPA(int id, String description, int ownWeight, int maxCargo, int roof) {
        super(id, description, TRAILER);
        this.ownWeight = ownWeight;
        this.maxCargo = maxCargo;
        this.roof = roof;
    }
    @Column(name="ownweight")
    public int getOwnWeight() {
        return ownWeight;
    }
    public void setOwnWeight(int ownWeight) {
        this.ownWeight = ownWeight;
    }
    @Column(name="maxcargo")
    public int getMaxCargo() {
        return maxCargo;
    }
    public void setMaxCargo(int maxCargo) {
        this.maxCargo = maxCargo;
    }
    @Column(name="roof")
    public int getRoof() {
        return roof;
    }
    public void setRoof(int roof) {
        this.roof = roof;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="toolasset")
@PrimaryKeyJoinColumn(name="id")
public class ToolAssetJPA extends AssetJPA {
    public static final int PERMOPEN = 1;
    public static final int COVERABLE = 2;
    public static final int REALROOF = 3;
    private String toolType;
    private String name;
    private int weight;
    public ToolAssetJPA() {
        this(0, "", "", "", 0);
    }
    public ToolAssetJPA(int id, String description, String toolType, String name, int weight) {
        super(id, description, TOOL);
        this.toolType = toolType;
        this.name = name;
        this.weight = weight;
    }
    @Column(name="tooltype")
    public String getToolType() {
        return toolType;
    }
    public void setToolType(String toolType) {
        this.toolType = toolType;
    }
    @Column(name="name")
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Column(name="weight")
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class AutoMap<TFROM,TTO> {
    public static class MethodPair {
        private Method getter;
        private Method setter;
        private boolean trim;
        public MethodPair(Method getter, Method setter, boolean trim) {
            this.getter = getter;
            this.setter = setter;
            this.trim = trim;
        }
        public Method getGetter() {
            return getter;
        }
        public Method getSetter() {
            return setter;
        }
        public boolean getTrim() {
            return trim;
        }
    }
    private List<MethodPair> conv;
    public AutoMap(Class<TFROM> from, Class<TTO> to) throws IntrospectionException {
        this(from, to, false);
    }
    public AutoMap(Class<TFROM> from, Class<TTO> to, boolean trim) throws IntrospectionException {
        conv = new ArrayList<MethodPair>();
        for(PropertyDescriptor pdfrom : Introspector.getBeanInfo(from).getPropertyDescriptors()) {
            for(PropertyDescriptor pdto : Introspector.getBeanInfo(to).getPropertyDescriptors()) {
                if(pdfrom.getName().equals(pdto.getName())) {
                    Method getter = pdfrom.getReadMethod();
                    Method setter = pdto.getWriteMethod();
                    if(getter != null && setter != null) {
                        conv.add(new MethodPair(getter, setter, trim && pdfrom.getPropertyType().equals(String.class)));
                    }
                }
            }
        }
    }
    public void convert(TFROM from, TTO to) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        for(MethodPair mp : conv) {
            if(mp.getTrim()) {
                mp.getSetter().invoke(to, ((String)mp.getGetter().invoke(from)).trim());
            } else {
                mp.getSetter().invoke(to, mp.getGetter().invoke(from));
            }
        }
    }
}
# stuff for JPA
from java.util.logging import Level
from java.util.logging import Logger
from javax.persistence import Persistence

# stuff for ISAM
from dk.vajhoej.isam import Key0
from dk.vajhoej.isam.local import LocalIsamSource

#input class
import AssetISAM
import TruckAssetISAM
import TrailerAssetISAM
import ToolAssetISAM
#output class
import AssetJPA
import TruckAssetJPA
import TrailerAssetJPA
import ToolAssetJPA
# auto map
import AutoMap

# open JPA
Logger.getLogger("org.hibernate").setLevel(Level.OFF)
emf = Persistence.createEntityManagerFactory("Asset")
em = emf.createEntityManager()

# open ISAM
db = LocalIsamSource('asset.isq', 'dk.vajhoej.vms.rms.IndexSequential', False)

# process all records
truckmapper = AutoMap(TruckAssetISAM, TruckAssetJPA, True)
trailermapper = AutoMap(TrailerAssetISAM, TrailerAssetJPA, True)
toolmapper = AutoMap(ToolAssetISAM, ToolAssetJPA, True)
it = db.readGE(AssetISAM, Key0(0))
em.getTransaction().begin()
while it.read():
    oldrec = it.current()
    if oldrec.assetType == 1:
        newrec = TruckAssetJPA()
        truckmapper.convert(oldrec, newrec)
    if oldrec.assetType == 2:
        newrec = TrailerAssetJPA()
        trailermapper.convert(oldrec, newrec)
    if oldrec.assetType == 3:
        newrec = ToolAssetJPA()
        toolmapper.convert(oldrec, newrec)
    em.persist(newrec)
it.close()
em.getTransaction().commit()

# close ISAM
db.close()

# close JPA
em.close()
emf.close()

META-INF/persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="Asset">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <class>TruckAssetJPA</class>
      <class>TrailerAssetJPA</class>
      <class>ToolAssetJPA</class>
      <exclude-unlisted-classes/>
      <properties>
          <!--<property name="show_sql" value="true"/>-->
          <property name="hibernate.connection.driver_class" value="org.sqlite.JDBC"/>
          <property name="hibernate.connection.url" value="jdbc:sqlite:asset.db"/>
          <property name="hibernate.connection.username" value=""/>
          <property name="hibernate.connection.password" value=""/>
          <property name="hibernate.dialect" value="org.hibernate.dialect.SQLiteDialect"/>
      </properties>
   </persistence-unit>
</persistence>

Database structure:

CREATE TABLE asset (
    id INTEGER NOT NULL,
    description VARCHAR(32),
    assettype INTEGER,
    PRIMARY KEY(id)
);
CREATE TABLE truckasset (
    id INTEGER NOT NULL,
    model VARCHAR(16),
    license VARCHAR(8),
    towcapacity INTEGER,
    PRIMARY KEY(id)
);
CREATE TABLE trailerasset (
    id INTEGER NOT NULL,
    ownweight INTEGER,
    maxcargo INTEGER,
    roof INTEGER,
    PRIMARY KEY(id)
);
CREATE TABLE toolasset (
    id INTEGER NOT NULL,
    tooltype VARCHAR(16),
    name VARCHAR(16),
    weight INTEGER,
    PRIMARY KEY(id)
);

After:

Insert:

asset.h:

#define TRUCK 1
#define TRAILER 2
#define TOOL 3

#define PERMOPEN 1
#define COVERABLE 2
#define REALROOF 3

struct truckasset
{
    char model[17];
    char license[9];
    int towcapacity;
};

struct trailerasset
{
    int ownweight;
    int maxcargo;
    int roof;
};

struct toolasset
{
    char tooltype[17];
    char name[17];
    int weight;
};

struct asset
{
    int id;
    char description[33];
    int assettype;
    union
    {
       struct truckasset truck;
       struct trailerasset trailer;
       struct toolasset tool;
    } u;
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "sqlite3.h"

#include "asset.h"

int main()
{
    sqlite3 *con;
    sqlite3_stmt *ins;
    sqlite3_stmt *toolins;
    char *sqlstr = "INSERT INTO asset VALUES(?,?,?)";
    char *toolsqlstr = "INSERT INTO toolasset VALUES(?,?,?,?)";
    int stat;
    struct asset a;
    stat = sqlite3_open("asset.db", &con);
    stat = sqlite3_prepare(con, sqlstr, strlen(sqlstr), &ins, NULL);
    stat = sqlite3_prepare(con, toolsqlstr, strlen(toolsqlstr), &toolins, NULL);
    a.id = 7;
    strcpy(a.description, "Shovel");
    a.assettype = TOOL;
    strcpy(a.u.tool.tooltype, "Shovel");
    strcpy(a.u.tool.name, "Noname");
    a.u.tool.weight = 5;
    sqlite3_bind_int(ins, 1, a.id);
    sqlite3_bind_text(ins, 2, a.description, strlen(a.description), NULL);
    sqlite3_bind_int(ins, 3, a.assettype);
    stat = sqlite3_step(ins);
    sqlite3_finalize(ins);
    sqlite3_bind_int(toolins, 1, a.id);
    sqlite3_bind_text(toolins, 2, a.u.tool.tooltype, strlen(a.u.tool.tooltype), NULL);
    sqlite3_bind_text(toolins, 3, a.u.tool.name, strlen(a.u.tool.name), NULL);
    sqlite3_bind_int(toolins, 4, a.u.tool.weight);
    stat = sqlite3_step(toolins);
    sqlite3_finalize(toolins);
    sqlite3_close(con);
    return 0;
}

Build and run:

$ cc/include=sqlite3_include: insert_after_1
$ link insert_after_1 + sys$input/opt
sqlite3shr/share
$
$ run insert_after_1

asset.pas:

const
   TRUCK = 1;
   TRAILER = 2;
   TOOL = 3;

const
   PERMOPEN = 1;
   COVERABLE = 2;
   REALROOF = 3;

type
   asset = record
               id : unsigned;
               description : varying[32] of char;
               assettype : integer;
               case integer of
                  TRUCK:
                     (
                        model : varying[16] of char;
                        license : varying[8] of char;
                        towcapacity : integer;
                     );
                  TRAILER:
                     (
                        ownweight : integer;
                        maxcargo : integer;
                        roof : integer;
                     );
                  TOOL:
                     (
                        tooltype : varying[16] of char;
                        name : varying[16] of char;
                        weight : integer;
                     );
            end;
[inherit('psqlitedir:sqlite', 'psqlitedir:psqlite')]
program insert_after_2(input, output);

%include 'asset.pas'

var
   con : sqlite_ptr;
   ins, toolins : sqlite_stmt_ptr;
   a : asset;

begin
   con := psqlite_open('asset.db');
   ins := psqlite_prepare(con, 'INSERT INTO asset VALUES(?,?,?)');
   toolins := psqlite_prepare(con, 'INSERT INTO toolasset VALUES(?,?,?,?)');
   a.id := 8;
   a.description := 'Rake';
   a.assettype := TOOL;
   a.tooltype := 'Rake';
   a.name := 'Noname';
   a.weight := 5;
   psqlite_bind_int(ins, 1, a.id);
   psqlite_bind_text(ins, 2, a.description);
   psqlite_bind_int(ins, 3, a.assettype);
   psqlite_step_nonquery(ins);
   psqlite_bind_int(toolins, 1, a.id);
   psqlite_bind_text(toolins, 2, a.tooltype);
   psqlite_bind_text(toolins, 3, a.name);
   psqlite_bind_int(toolins, 4, a.weight);
   psqlite_step_nonquery(toolins);
   psqlite_finalize(ins);
   psqlite_finalize(toolins);
   psqlite_close(con);
end.

Build and run:

$ pas insert_after_2
$ link insert_after_2 + sys$input/opt + psqlitedir:sqlite/opt
psqlitedir:psqlite
psqlitedir:sqlite
$
$ run insert_after_2

asset.py:

TRUCK = 1
TRAILER = 2
TOOL = 3

PERMOPEN = 1
COVERABLE = 2
REALROOF = 3

class Asset(object):
    def __init__(self, _id = 0, _description = '', _assettype = 0):
        self.id = _id
        self.description = _description
        self.assettype = _assettype

class TruckAsset(Asset):
    def __init__(self, _id = 0, _description = '', _model = '', _license = '', _towcapacity = 0):
        super(TruckAsset, self).__init__(_id, _description, TRUCK)
        self.model = _model
        self.license = _license
        self.towcapacity = _towcapacity

class TrailerAsset(Asset):
    def __init__(self, _id = 0, _description = '', _ownweight = 0, _maxcargo = 0, _roof = 0):
        super(TrailerAsset, self).__init__(_id, _description, TRAILER)
        self.ownweight = _ownweight
        self.maxcargo = _maxcargo
        self.roof = _roof

class ToolAsset(Asset):
    def __init__(self, _id = 0, _description = '', _tooltype = '', _name = '', _weight = 0):
        super(ToolAsset, self).__init__(_id, _description, TOOL)
        self.tooltype = _tooltype
        self.name = _name
        self.weight = _weight
import sqlite3

from asset import *

con = sqlite3.connect('asset.db')
c = con.cursor()
a = ToolAsset(9, 'Craftsman snowblower', 'Snowblower', 'Craftsman', 200)
c.execute('INSERT INTO asset VALUES(?,?,?)', (a.id, a.description, a.assettype))
c.execute('INSERT INTO toolasset VALUES(?,?,?,?)', (a.id, a.tooltype, a.name, a.weight))
con.commit()
con.close()

asset.php:

<?php
define('TRUCK', 1);
define('TRAILER', 2);
define('TOOL', 3);

define('PERMOPEN', 1);
define('COVERABLE', 2);
define('REALROOF', 3);

class Asset {
    public $id;
    public $description;
    public $assetType;
    public function __construct($id = 0, $description = '', $assetType = '') {
        $this->id = $id;
        $this->description = $description;
        $this->assetType = $assetType;
    }
}

class TruckAsset extends Asset {
    public $model;
    public $license;
    public $towcapacity;
    public function __construct($id = 0, $description = '', $model = '', $license = '', $towcapacity = 0) {
        parent::__construct($id, $description, TRUCK);
        $this->model = $model;
        $this->license = $license;
        $this->towcapacity = $towcapacity;
    }
}

class TrailerAsset extends Asset {
    public $ownWeight;
    public $maxCargo;
    public $roof;
    public function __construct($id = 0, $description = '', $ownWeight = 0, $maxCargo = 0, $roof = 0) {
        parent::__construct($id, $description, TRAILER);
        $this->ownWeight = $ownWeight;
        $this->maxCargo = $maxCargo;
        $this->roof = $roof;
    }
}

class ToolAsset extends Asset {
    public $toolType;
    public $name;
    public $weight;
    public function __construct($id = 0, $description = '', $toolType = '', $name = '', $weight = 0) {
        parent::__construct($id, $description, TOOL);
        $this->toolType = $toolType;
        $this->name = $name;
        $this->weight = $weight;
    }
}

?>
<?php
include '/disk2/arne/migr/inher/asset.php';

$con = new PDO('sqlite:/disk2/arne/migr/inher/asset.db');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$stmt = $con->prepare('INSERT INTO asset VALUES(:ID,:DESCRIPTION,:ASSETTYPE)');
$toolstmt = $con->prepare('INSERT INTO toolasset VALUES(:ID,:TOOLTYPE,:NAME,:WEIGHT)');
$a = new ToolAsset(10, 'Normal wheelbarrow', 'Wheelbarrow', 'Noname', 25);
$stmt->execute(array(':ID' => $a->id, ':DESCRIPTION' => $a->description, ':ASSETTYPE' => $a->assetType));
$toolstmt->execute(array(':ID' => $a->id, ':TOOLTYPE' => $a->toolType, ':NAME' => $a->name, ':WEIGHT' => $a->weight));
?>
public class Asset {
    public static final int TRUCK = 1;
    public static final int TRAILER = 2;
    public static final int TOOL = 3;
    private int id;
    private String description;
    private int assetType;
    public Asset() {
        this(0, "", 0);
    }
    public Asset(int id, String description, int assetType) {
        this.id = id;
        this.description = description;
        this.assetType = assetType;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public int getAssetType() {
        return assetType;
    }
    public void setAssetType(int assetType) {
        this.assetType = assetType;
    }
}
public class TruckAsset extends Asset {
    private String model;
    private String license;
    private int towcapacity;
    public TruckAsset() {
        this(0, "", "", "", 0);
    }
    public TruckAsset(int id, String description, String model, String license, int towcapacity) {
        super(id, description, TRUCK);
        this.model = model;
        this.license = license;
        this.towcapacity = towcapacity;
    }
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    public String getLicense() {
        return license;
    }
    public void setLicense(String license) {
        this.license = license;
    }
    public int getTowcapacity() {
        return towcapacity;
    }
    public void setTowcapacity(int towcapacity) {
        this.towcapacity = towcapacity;
    }
}
public class TrailerAsset extends Asset {
    private int ownWeight;
    private int maxCargo;
    private int roof;
    public TrailerAsset() {
        this(0, "", 0, 0, 0);
    }
    public TrailerAsset(int id, String description, int ownWeight, int maxCargo, int roof) {
        super(id, description, TRAILER);
        this.ownWeight = ownWeight;
        this.maxCargo = maxCargo;
        this.roof = roof;
    }
    public int getOwnWeight() {
        return ownWeight;
    }
    public void setOwnWeight(int ownWeight) {
        this.ownWeight = ownWeight;
    }
    public int getMaxCargo() {
        return maxCargo;
    }
    public void setMaxCargo(int maxCargo) {
        this.maxCargo = maxCargo;
    }
    public int getRoof() {
        return roof;
    }
    public void setRoof(int roof) {
        this.roof = roof;
    }
}
public class ToolAsset extends Asset {
    public static final int PERMOPEN = 1;
    public static final int COVERABLE = 2;
    public static final int REALROOF = 3;
    private String toolType;
    private String name;
    private int weight;
    public ToolAsset() {
        this(0, "", "", "", 0);
    }
    public ToolAsset(int id, String description, String toolType, String name, int weight) {
        super(id, description, TOOL);
        this.toolType = toolType;
        this.name = name;
        this.weight = weight;
    }
    public String getToolType() {
        return toolType;
    }
    public void setToolType(String toolType) {
        this.toolType = toolType;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;

public class InsertAfter5 {
    public static void main(String[] args) throws Exception {
        Class.forName("org.sqlite.JDBC");
        Connection con = DriverManager.getConnection("jdbc:sqlite:asset.db");
        PreparedStatement pstmt = con.prepareStatement("INSERT INTO asset VALUES(?,?,?)");
        PreparedStatement toolpstmt = con.prepareStatement("INSERT INTO toolasset VALUES(?,?,?,?)");
        ToolAsset a = new ToolAsset(11, "Echo edger", "Edger", "Echo", 25);
        pstmt.setInt(1, a.getId());
        pstmt.setString(2, a.getDescription());
        pstmt.setInt(3, a.getAssetType());
        pstmt.executeUpdate();
        toolpstmt.setInt(1, a.getId());
        toolpstmt.setString(2, a.getToolType());
        toolpstmt.setString(3, a.getName());
        toolpstmt.setInt(4, a.getWeight());
        toolpstmt.executeUpdate();
        pstmt.close();
        toolpstmt.close();
        con.close();
    }
}

Build and run:

$ javac InsertAfter5.java Asset.java TruckAsset.java TrailerAsset.java ToolAsset.java
$ java -cp .:/javalib/sqlite-jdbc-3_14_1-vms.jar "InsertAfter5"
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="asset")
@Inheritance(strategy=InheritanceType.JOINED)
public class AssetJPA {
    public static final int TRUCK = 1;
    public static final int TRAILER = 2;
    public static final int TOOL = 3;
    private int id;
    private String description;
    private int assetType;
    public AssetJPA() {
        this(0, "", 0);
    }
    public AssetJPA(int id, String description, int assetType) {
        this.id = id;
        this.description = description;
        this.assetType = assetType;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="description")
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    @Column(name="assettype")
    public int getAssetType() {
        return assetType;
    }
    public void setAssetType(int assetType) {
        this.assetType = assetType;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="truckasset")
@PrimaryKeyJoinColumn(name="id")
public class TruckAssetJPA extends AssetJPA {
    private String model;
    private String license;
    private int towcapacity;
    public TruckAssetJPA() {
        this(0, "", "", "", 0);
    }
    public TruckAssetJPA(int id, String description, String model, String license, int towcapacity) {
        super(id, description, TRUCK);
        this.model = model;
        this.license = license;
        this.towcapacity = towcapacity;
    }
    @Column(name="model")
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    @Column(name="license")
    public String getLicense() {
        return license;
    }
    public void setLicense(String license) {
        this.license = license;
    }
    @Column(name="towcapacity")
    public int getTowcapacity() {
        return towcapacity;
    }
    public void setTowcapacity(int towcapacity) {
        this.towcapacity = towcapacity;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="trailerasset")
@PrimaryKeyJoinColumn(name="id")
public class TrailerAssetJPA extends AssetJPA {
    private int ownWeight;
    private int maxCargo;
    private int roof;
    public TrailerAssetJPA() {
        this(0, "", 0, 0, 0);
    }
    public TrailerAssetJPA(int id, String description, int ownWeight, int maxCargo, int roof) {
        super(id, description, TRAILER);
        this.ownWeight = ownWeight;
        this.maxCargo = maxCargo;
        this.roof = roof;
    }
    @Column(name="ownweight")
    public int getOwnWeight() {
        return ownWeight;
    }
    public void setOwnWeight(int ownWeight) {
        this.ownWeight = ownWeight;
    }
    @Column(name="maxcargo")
    public int getMaxCargo() {
        return maxCargo;
    }
    public void setMaxCargo(int maxCargo) {
        this.maxCargo = maxCargo;
    }
    @Column(name="roof")
    public int getRoof() {
        return roof;
    }
    public void setRoof(int roof) {
        this.roof = roof;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="toolasset")
@PrimaryKeyJoinColumn(name="id")
public class ToolAssetJPA extends AssetJPA {
    public static final int PERMOPEN = 1;
    public static final int COVERABLE = 2;
    public static final int REALROOF = 3;
    private String toolType;
    private String name;
    private int weight;
    public ToolAssetJPA() {
        this(0, "", "", "", 0);
    }
    public ToolAssetJPA(int id, String description, String toolType, String name, int weight) {
        super(id, description, TOOL);
        this.toolType = toolType;
        this.name = name;
        this.weight = weight;
    }
    @Column(name="tooltype")
    public String getToolType() {
        return toolType;
    }
    public void setToolType(String toolType) {
        this.toolType = toolType;
    }
    @Column(name="name")
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Column(name="weight")
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;

public class InsertAfter6 {
    public static void main(String[] args) throws Exception {
        Logger.getLogger("org.hibernate").setLevel(Level.OFF);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Asset");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        AssetJPA a = new ToolAssetJPA(12, "Echo blower", "Blower", "Echo", 50);
        em.persist(a);
        em.getTransaction().commit();
        em.close();
        emf.close();
    }
}

Build and run:

$ javac -cp 'hibpath' InsertAfter6.java AssetJPA.java TruckAssetJPA.java TrailerAssetJPA.java ToolAssetJPA.java
$ java -cp .:'hibpath':/javalib/sqlite-jdbc-3_14_1-vms.jar:/javalib/sqlitehib.jar "InsertAfter6"

List:

asset.h:

#define TRUCK 1
#define TRAILER 2
#define TOOL 3

#define PERMOPEN 1
#define COVERABLE 2
#define REALROOF 3

struct truckasset
{
    char model[17];
    char license[9];
    int towcapacity;
};

struct trailerasset
{
    int ownweight;
    int maxcargo;
    int roof;
};

struct toolasset
{
    char tooltype[17];
    char name[17];
    int weight;
};

struct asset
{
    int id;
    char description[33];
    int assettype;
    union
    {
       struct truckasset truck;
       struct trailerasset trailer;
       struct toolasset tool;
    } u;
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "sqlite3.h"

#include "asset.h"

int main()
{
    sqlite3 *con;
    sqlite3_stmt *trucksel;
    sqlite3_stmt *trailersel;
    sqlite3_stmt *toolsel;
    char *trucksqlstr = "SELECT asset.id,asset.description,asset.assettype,truckasset.model,truckasset.license,truckasset.towcapacity FROM asset JOIN truckasset ON asset.id = truckasset.id";
    char *trailersqlstr = "SELECT asset.id,asset.description,asset.assettype,trailerasset.ownweight,trailerasset.maxcargo,trailerasset.roof FROM asset JOIN trailerasset ON asset.id = trailerasset.id";
    char *toolsqlstr = "SELECT asset.id,asset.description,asset.assettype,toolasset.tooltype,toolasset.name,toolasset.weight FROM asset JOIN toolasset ON asset.id = toolasset.id";
    int stat;
    int count;
    struct asset res[1000];
    int i;
    stat = sqlite3_open("asset.db", &con);
    count = 0;
    stat = sqlite3_prepare(con, trucksqlstr, strlen(trucksqlstr), &trucksel, NULL);
    while(sqlite3_step(trucksel) == SQLITE_ROW)
    {
        count++;
        res[count-1].id = sqlite3_column_int(trucksel, 0);
        strcpy(res[count-1].description, (char *)sqlite3_column_text(trucksel, 1));
        res[count-1].assettype = sqlite3_column_int(trucksel, 2);
        strcpy(res[count-1].u.truck.model, (char *)sqlite3_column_text(trucksel, 3));
        strcpy(res[count-1].u.truck.license, (char *)sqlite3_column_text(trucksel, 4));
        res[count-1].u.truck.towcapacity = sqlite3_column_int(trucksel, 5);
    }
    sqlite3_finalize(trucksel);
    stat = sqlite3_prepare(con, trailersqlstr, strlen(trailersqlstr), &trailersel, NULL);
    while(sqlite3_step(trailersel) == SQLITE_ROW)
    {
        count++;
        res[count-1].id = sqlite3_column_int(trailersel, 0);
        strcpy(res[count-1].description, (char *)sqlite3_column_text(trailersel, 1));
        res[count-1].assettype = sqlite3_column_int(trailersel, 2);
        res[count-1].u.trailer.ownweight = sqlite3_column_int(trailersel, 3);
        res[count-1].u.trailer.maxcargo = sqlite3_column_int(trailersel, 4);
        res[count-1].u.trailer.roof = sqlite3_column_int(trailersel, 5);
    }
    sqlite3_finalize(trailersel);
    stat = sqlite3_prepare(con, toolsqlstr, strlen(toolsqlstr), &toolsel, NULL);
    while(sqlite3_step(toolsel) == SQLITE_ROW)
    {
        count++;
        res[count-1].id = sqlite3_column_int(toolsel, 0);
        strcpy(res[count-1].description, (char *)sqlite3_column_text(toolsel, 1));
        res[count-1].assettype = sqlite3_column_int(toolsel, 2);
        strcpy(res[count-1].u.tool.tooltype, (char *)sqlite3_column_text(toolsel, 3));
        strcpy(res[count-1].u.tool.name, (char *)sqlite3_column_text(toolsel, 4));
        res[count-1].u.tool.weight = sqlite3_column_int(toolsel, 5);
    }
    sqlite3_finalize(toolsel);
    sqlite3_close(con);
    for(i = 0; i < count; i++)
    {
        switch(res[i].assettype)
        {
            case TRUCK:
                printf("%d %s %s %s %d\n", res[i].id, res[i].description, res[i].u.truck.model,res[i].u.truck.license, res[i].u.truck.towcapacity);
                break;
            case TRAILER:
                printf("%d %s %d %d %d\n", res[i].id, res[i].description, res[i].u.trailer.ownweight,res[i].u.trailer.maxcargo, res[i].u.trailer.roof );
                break;
            case TOOL:
                printf("%d %s %s %s %d\n", res[i].id, res[i].description, res[i].u.tool.tooltype,res[i].u.tool.name, res[i].u.tool.weight );
                break;
        }
    }
    return 0;
}

Build and run:

$ cc/include=sqlite3_include: list_after_1
$ link list_after_1 + sys$input/opt
sqlite3shr/share
$
$ run list_after_1

asset.pas:

const
   TRUCK = 1;
   TRAILER = 2;
   TOOL = 3;

const
   PERMOPEN = 1;
   COVERABLE = 2;
   REALROOF = 3;

type
   asset = record
               id : unsigned;
               description : varying[32] of char;
               assettype : integer;
               case integer of
                  TRUCK:
                     (
                        model : varying[16] of char;
                        license : varying[8] of char;
                        towcapacity : integer;
                     );
                  TRAILER:
                     (
                        ownweight : integer;
                        maxcargo : integer;
                        roof : integer;
                     );
                  TOOL:
                     (
                        tooltype : varying[16] of char;
                        name : varying[16] of char;
                        weight : integer;
                     );
            end;
[inherit('psqlitedir:sqlite', 'psqlitedir:psqlite')]
program list_after_2(input, output);

%include 'asset.pas'

var
   con : sqlite_ptr;
   sel : sqlite_stmt_ptr;
   count : integer;
   res : array [1..1000] of asset;
   i : integer;

begin
   con := psqlite_open('asset.db');
   count := 0;
   sel := psqlite_prepare(con, 'SELECT asset.id,asset.description,asset.assettype,truckasset.model,truckasset.license,truckasset.towcapacity FROM asset JOIN truckasset ON asset.id = truckasset.id');
   while psqlite_step_query(sel) do begin
      count := count + 1;
      res[count].id := psqlite_column_int(sel, 0);
      res[count].description := psqlite_column_text(sel, 1);
      res[count].assettype := psqlite_column_int(sel, 2);
      res[count].model := psqlite_column_text(sel, 3);
      res[count].license := psqlite_column_text(sel, 4);
      res[count].towcapacity := psqlite_column_int(sel, 5);
   end;
   psqlite_finalize(sel);
   sel := psqlite_prepare(con, 'SELECT asset.id,asset.description,asset.assettype,trailerasset.ownweight,trailerasset.maxcargo,trailerasset.roof FROM asset JOIN trailerasset ON asset.id = trailerasset.id');
   while psqlite_step_query(sel) do begin
      count := count + 1;
      res[count].id := psqlite_column_int(sel, 0);
      res[count].description := psqlite_column_text(sel, 1);
      res[count].assettype := psqlite_column_int(sel, 2);
      res[count].ownweight := psqlite_column_int(sel, 3);
      res[count].maxcargo := psqlite_column_int(sel, 4);
      res[count].roof := psqlite_column_int(sel, 5);
   end;
   psqlite_finalize(sel);
   sel := psqlite_prepare(con, 'SELECT asset.id,asset.description,asset.assettype,toolasset.tooltype,toolasset.name,toolasset.weight FROM asset JOIN toolasset ON asset.id = toolasset.id');
   while psqlite_step_query(sel) do begin
      count := count + 1;
      res[count].id := psqlite_column_int(sel, 0);
      res[count].description := psqlite_column_text(sel, 1);
      res[count].assettype := psqlite_column_int(sel, 2);
      res[count].tooltype := psqlite_column_text(sel, 3);
      res[count].name := psqlite_column_text(sel, 4);
      res[count].weight := psqlite_column_int(sel, 5);
   end;
   psqlite_finalize(sel);
   psqlite_close(con);
   for i := 1 to count do begin
      write(res[i].id, ' ', res[i].description, ' ');
      case res[i].assettype of
         TRUCK:
            begin
               write(res[i].model, ' ', res[i].license, ' ', res[i].towcapacity);
            end;
         TRAILER:
            begin
               write(res[i].ownweight, ' ', res[i].maxcargo, ' ', res[i].roof);
            end;
         TOOL:
            begin
               write(res[i].tooltype, ' ', res[i].name, ' ', res[i].weight);
            end;
      end;
      writeln;
   end;
end.

Build and run:

$ pas list_after_2
$ link list_after_2 + sys$input/opt + psqlitedir:sqlite/opt
psqlitedir:psqlite
psqlitedir:sqlite
$
$ run list_after_2

asset.py:

TRUCK = 1
TRAILER = 2
TOOL = 3

PERMOPEN = 1
COVERABLE = 2
REALROOF = 3

class Asset(object):
    def __init__(self, _id = 0, _description = '', _assettype = 0):
        self.id = _id
        self.description = _description
        self.assettype = _assettype

class TruckAsset(Asset):
    def __init__(self, _id = 0, _description = '', _model = '', _license = '', _towcapacity = 0):
        super(TruckAsset, self).__init__(_id, _description, TRUCK)
        self.model = _model
        self.license = _license
        self.towcapacity = _towcapacity

class TrailerAsset(Asset):
    def __init__(self, _id = 0, _description = '', _ownweight = 0, _maxcargo = 0, _roof = 0):
        super(TrailerAsset, self).__init__(_id, _description, TRAILER)
        self.ownweight = _ownweight
        self.maxcargo = _maxcargo
        self.roof = _roof

class ToolAsset(Asset):
    def __init__(self, _id = 0, _description = '', _tooltype = '', _name = '', _weight = 0):
        super(ToolAsset, self).__init__(_id, _description, TOOL)
        self.tooltype = _tooltype
        self.name = _name
        self.weight = _weight
import sqlite3

from asset import *

con = sqlite3.connect('asset.db')
c = con.cursor()
res = []
c.execute('SELECT asset.id,asset.description,asset.assettype,truckasset.model,truckasset.license,truckasset.towcapacity FROM asset JOIN truckasset ON asset.id = truckasset.id')
for row in c.fetchall():
    res.append(TruckAsset(row[0], row[1], row[3], row[4], row[5]))
c.execute('SELECT asset.id,asset.description,asset.assettype,trailerasset.ownweight,trailerasset.maxcargo,trailerasset.roof FROM asset JOIN trailerasset ON asset.id = trailerasset.id')
for row in c.fetchall():
    res.append(TrailerAsset(row[0], row[1], row[3], row[4], row[5]))
c.execute('SELECT asset.id,asset.description,asset.assettype,toolasset.tooltype,toolasset.name,toolasset.weight FROM asset JOIN toolasset ON asset.id = toolasset.id')
for row in c.fetchall():
    res.append(ToolAsset(row[0], row[1], row[3], row[4], row[5]))
con.commit()
con.close()

for o in res:
    if isinstance(o, TruckAsset):
        print('%d %s %s %s %d' % (o.id, o.description, o.model, o.license, o.towcapacity))
    if isinstance(o, TrailerAsset):                    
        print('%d %s %d %d %d' % (o.id, o.description, o.ownweight, o.maxcargo, o.roof))
    if isinstance(o, ToolAsset):
        print('%d %s %s %s %d' % (o.id, o.description, o.tooltype, o.name, o.weight))

asset.php:

<?php
define('TRUCK', 1);
define('TRAILER', 2);
define('TOOL', 3);

define('PERMOPEN', 1);
define('COVERABLE', 2);
define('REALROOF', 3);

class Asset {
    public $id;
    public $description;
    public $assetType;
    public function __construct($id = 0, $description = '', $assetType = '') {
        $this->id = $id;
        $this->description = $description;
        $this->assetType = $assetType;
    }
}

class TruckAsset extends Asset {
    public $model;
    public $license;
    public $towcapacity;
    public function __construct($id = 0, $description = '', $model = '', $license = '', $towcapacity = 0) {
        parent::__construct($id, $description, TRUCK);
        $this->model = $model;
        $this->license = $license;
        $this->towcapacity = $towcapacity;
    }
}

class TrailerAsset extends Asset {
    public $ownWeight;
    public $maxCargo;
    public $roof;
    public function __construct($id = 0, $description = '', $ownWeight = 0, $maxCargo = 0, $roof = 0) {
        parent::__construct($id, $description, TRAILER);
        $this->ownWeight = $ownWeight;
        $this->maxCargo = $maxCargo;
        $this->roof = $roof;
    }
}

class ToolAsset extends Asset {
    public $toolType;
    public $name;
    public $weight;
    public function __construct($id = 0, $description = '', $toolType = '', $name = '', $weight = 0) {
        parent::__construct($id, $description, TOOL);
        $this->toolType = $toolType;
        $this->name = $name;
        $this->weight = $weight;
    }
}

?>
<?php
include '/disk2/arne/migr/inher/asset.php';

$con = new PDO('sqlite:/disk2/arne/migr/inher/asset.db');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_NUM);

$res = array();
$stmt = $con->prepare('SELECT asset.id,asset.description,asset.assettype,truckasset.model,truckasset.license,truckasset.towcapacity FROM asset JOIN truckasset ON asset.id = truckasset.id');
$stmt->execute(array());
while($row = $stmt->fetch()) {
   $res[] = new TruckAsset($row[0], $row[1], $row[3], $row[4], $row[5]);
}
$stmt = $con->prepare('SELECT asset.id,asset.description,asset.assettype,trailerasset.ownweight,trailerasset.maxcargo,trailerasset.roof FROM asset JOIN trailerasset ON asset.id = trailerasset.id');
$stmt->execute(array());
while($row = $stmt->fetch()) {
   $res[] = new TrailerAsset($row[0], $row[1], $row[3], $row[4], $row[5]);
}
$stmt = $con->prepare('SELECT asset.id,asset.description,asset.assettype,toolasset.tooltype,toolasset.name,toolasset.weight FROM asset JOIN toolasset ON asset.id = toolasset.id');
$stmt->execute(array());
while($row = $stmt->fetch()) {
   $res[] = new ToolAsset($row[0], $row[1], $row[3], $row[4], $row[5]);
}

foreach($res as $o) {
    if($o instanceof TruckAsset) {
        echo sprintf('%d %s %s %s %d', $o->id, $o->description, $o->model, $o->license, $o->towcapacity) . "\r\n";
    }
    if($o instanceof TrailerAsset) {
        echo sprintf('%d %s %d %d %d', $o->id, $o->description, $o->ownWeight, $o->maxCargo, $o->roof) . "\r\n";
    }
    if($o instanceof ToolAsset) {
        echo sprintf('%d %s %s %s %d', $o->id, $o->description, $o->toolType, $o->name, $o->weight) . "\r\n";
    }
}
?>
public class Asset {
    public static final int TRUCK = 1;
    public static final int TRAILER = 2;
    public static final int TOOL = 3;
    private int id;
    private String description;
    private int assetType;
    public Asset() {
        this(0, "", 0);
    }
    public Asset(int id, String description, int assetType) {
        this.id = id;
        this.description = description;
        this.assetType = assetType;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public int getAssetType() {
        return assetType;
    }
    public void setAssetType(int assetType) {
        this.assetType = assetType;
    }
}
public class TruckAsset extends Asset {
    private String model;
    private String license;
    private int towcapacity;
    public TruckAsset() {
        this(0, "", "", "", 0);
    }
    public TruckAsset(int id, String description, String model, String license, int towcapacity) {
        super(id, description, TRUCK);
        this.model = model;
        this.license = license;
        this.towcapacity = towcapacity;
    }
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    public String getLicense() {
        return license;
    }
    public void setLicense(String license) {
        this.license = license;
    }
    public int getTowcapacity() {
        return towcapacity;
    }
    public void setTowcapacity(int towcapacity) {
        this.towcapacity = towcapacity;
    }
}
public class TrailerAsset extends Asset {
    private int ownWeight;
    private int maxCargo;
    private int roof;
    public TrailerAsset() {
        this(0, "", 0, 0, 0);
    }
    public TrailerAsset(int id, String description, int ownWeight, int maxCargo, int roof) {
        super(id, description, TRAILER);
        this.ownWeight = ownWeight;
        this.maxCargo = maxCargo;
        this.roof = roof;
    }
    public int getOwnWeight() {
        return ownWeight;
    }
    public void setOwnWeight(int ownWeight) {
        this.ownWeight = ownWeight;
    }
    public int getMaxCargo() {
        return maxCargo;
    }
    public void setMaxCargo(int maxCargo) {
        this.maxCargo = maxCargo;
    }
    public int getRoof() {
        return roof;
    }
    public void setRoof(int roof) {
        this.roof = roof;
    }
}
public class ToolAsset extends Asset {
    public static final int PERMOPEN = 1;
    public static final int COVERABLE = 2;
    public static final int REALROOF = 3;
    private String toolType;
    private String name;
    private int weight;
    public ToolAsset() {
        this(0, "", "", "", 0);
    }
    public ToolAsset(int id, String description, String toolType, String name, int weight) {
        super(id, description, TOOL);
        this.toolType = toolType;
        this.name = name;
        this.weight = weight;
    }
    public String getToolType() {
        return toolType;
    }
    public void setToolType(String toolType) {
        this.toolType = toolType;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class ListAfter5 {
    public static void main(String[] args) throws Exception {
        Class.forName("org.sqlite.JDBC");
        List<Asset> res = new ArrayList<Asset>();
        Connection con = DriverManager.getConnection("jdbc:sqlite:asset.db");
        Statement stmt = con.createStatement();
        ResultSet rs;
        rs = stmt.executeQuery("SELECT asset.id,asset.description,asset.assettype,truckasset.model,truckasset.license,truckasset.towcapacity FROM asset JOIN truckasset ON asset.id = truckasset.id");
        while(rs.next()) {
            int id = rs.getInt(1);
            String description = rs.getString(2);
            int assettype = rs.getInt(3);
            String model = rs.getString(4);
            String license = rs.getString(5);
            int towcapacity = rs.getInt(6);
            res.add(new TruckAsset(id, description, model, license, towcapacity));
        }
        rs.close();
        rs = stmt.executeQuery("SELECT asset.id,asset.description,asset.assettype,trailerasset.ownweight,trailerasset.maxcargo,trailerasset.roof FROM asset JOIN trailerasset ON asset.id = trailerasset.id");
        while(rs.next()) {
            int id = rs.getInt(1);
            String description = rs.getString(2);
            int assettype = rs.getInt(3);
            int ownweight = rs.getInt(4);
            int maxcargo = rs.getInt(5);
            int roof = rs.getInt(6);
            res.add(new TrailerAsset(id, description, ownweight, maxcargo, roof));
        }
        rs.close();
        rs = stmt.executeQuery("SELECT asset.id,asset.description,asset.assettype,toolasset.tooltype,toolasset.name,toolasset.weight FROM asset JOIN toolasset ON asset.id = toolasset.id");
        while(rs.next()) {
            int id = rs.getInt(1);
            String description = rs.getString(2);
            int assettype = rs.getInt(3);
            String tooltype = rs.getString(4);
            String name = rs.getString(5);
            int weight = rs.getInt(6);
            res.add(new ToolAsset(id, description, tooltype, name, weight));
        }
        rs.close();
        stmt.close();
        con.close();
        for(Asset o : res) {
            if(o instanceof TruckAsset) {
                TruckAsset o2 = (TruckAsset)o;
                System.out.printf("%d %s %s %s %d\n", o2.getId(), o2.getDescription(), o2.getModel(), o2.getLicense(), o2.getTowcapacity());
            }
            if(o instanceof TrailerAsset) {
                TrailerAsset o2 = (TrailerAsset)o;
                System.out.printf("%d %s %d %d %d\n", o2.getId(), o2.getDescription(), o2.getOwnWeight(), o2.getMaxCargo(), o2.getRoof());
            }
            if(o instanceof ToolAsset) {
                ToolAsset o2 = (ToolAsset)o;
                System.out.printf("%d %s %s %s %d\n", o2.getId(), o2.getDescription(), o2.getToolType(), o2.getName(), o2.getWeight());
            }
        }
    }
}

Build and run:

$ javac ListAfter5.java Asset.java TruckAsset.java TrailerAsset.java ToolAsset.java
$ java -cp .:/javalib/sqlite-jdbc-3_14_1-vms.jar "ListAfter5"
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="asset")
@Inheritance(strategy=InheritanceType.JOINED)
public class AssetJPA {
    public static final int TRUCK = 1;
    public static final int TRAILER = 2;
    public static final int TOOL = 3;
    private int id;
    private String description;
    private int assetType;
    public AssetJPA() {
        this(0, "", 0);
    }
    public AssetJPA(int id, String description, int assetType) {
        this.id = id;
        this.description = description;
        this.assetType = assetType;
    }
    @Id
    @Column(name="id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="description")
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    @Column(name="assettype")
    public int getAssetType() {
        return assetType;
    }
    public void setAssetType(int assetType) {
        this.assetType = assetType;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="truckasset")
@PrimaryKeyJoinColumn(name="id")
public class TruckAssetJPA extends AssetJPA {
    private String model;
    private String license;
    private int towcapacity;
    public TruckAssetJPA() {
        this(0, "", "", "", 0);
    }
    public TruckAssetJPA(int id, String description, String model, String license, int towcapacity) {
        super(id, description, TRUCK);
        this.model = model;
        this.license = license;
        this.towcapacity = towcapacity;
    }
    @Column(name="model")
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    @Column(name="license")
    public String getLicense() {
        return license;
    }
    public void setLicense(String license) {
        this.license = license;
    }
    @Column(name="towcapacity")
    public int getTowcapacity() {
        return towcapacity;
    }
    public void setTowcapacity(int towcapacity) {
        this.towcapacity = towcapacity;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="trailerasset")
@PrimaryKeyJoinColumn(name="id")
public class TrailerAssetJPA extends AssetJPA {
    private int ownWeight;
    private int maxCargo;
    private int roof;
    public TrailerAssetJPA() {
        this(0, "", 0, 0, 0);
    }
    public TrailerAssetJPA(int id, String description, int ownWeight, int maxCargo, int roof) {
        super(id, description, TRAILER);
        this.ownWeight = ownWeight;
        this.maxCargo = maxCargo;
        this.roof = roof;
    }
    @Column(name="ownweight")
    public int getOwnWeight() {
        return ownWeight;
    }
    public void setOwnWeight(int ownWeight) {
        this.ownWeight = ownWeight;
    }
    @Column(name="maxcargo")
    public int getMaxCargo() {
        return maxCargo;
    }
    public void setMaxCargo(int maxCargo) {
        this.maxCargo = maxCargo;
    }
    @Column(name="roof")
    public int getRoof() {
        return roof;
    }
    public void setRoof(int roof) {
        this.roof = roof;
    }
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="toolasset")
@PrimaryKeyJoinColumn(name="id")
public class ToolAssetJPA extends AssetJPA {
    public static final int PERMOPEN = 1;
    public static final int COVERABLE = 2;
    public static final int REALROOF = 3;
    private String toolType;
    private String name;
    private int weight;
    public ToolAssetJPA() {
        this(0, "", "", "", 0);
    }
    public ToolAssetJPA(int id, String description, String toolType, String name, int weight) {
        super(id, description, TOOL);
        this.toolType = toolType;
        this.name = name;
        this.weight = weight;
    }
    @Column(name="tooltype")
    public String getToolType() {
        return toolType;
    }
    public void setToolType(String toolType) {
        this.toolType = toolType;
    }
    @Column(name="name")
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Column(name="weight")
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;

public class ListAfter6 {
    public static void main(String[] args) throws Exception {
        Logger.getLogger("org.hibernate").setLevel(Level.OFF);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Asset");
        EntityManager em = emf.createEntityManager();
        TypedQuery<AssetJPA> q = em.createQuery("SELECT o FROM AssetJPA AS o", AssetJPA.class);
        List<AssetJPA> res = q.getResultList();
        for(AssetJPA o : res) {
            if(o instanceof TruckAssetJPA) {
                TruckAssetJPA o2 = (TruckAssetJPA)o;
                System.out.printf("%d %s %s %s %d\n", o2.getId(), o2.getDescription(), o2.getModel(), o2.getLicense(), o2.getTowcapacity());
            }
            if(o instanceof TrailerAssetJPA) {
                TrailerAssetJPA o2 = (TrailerAssetJPA)o;
                System.out.printf("%d %s %d %d %d\n", o2.getId(), o2.getDescription(), o2.getOwnWeight(), o2.getMaxCargo(), o2.getRoof());
            }
            if(o instanceof ToolAssetJPA) {
                ToolAssetJPA o2 = (ToolAssetJPA)o;
                System.out.printf("%d %s %s %s %d\n", o2.getId(), o2.getDescription(), o2.getToolType(), o2.getName(), o2.getWeight());
            }
        }
        em.close();
        emf.close();
    }
}

Build and run:

$ javac -cp 'hibpath' ListAfter6.java AssetJPA.java TruckAssetJPA.java TrailerAssetJPA.java ToolAssetJPA.java
$ java -cp .:'hibpath':/javalib/sqlite-jdbc-3_14_1-vms.jar:/javalib/sqlitehib.jar "ListAfter6"

Complex queries:

We started by making the claim that a relational database is much better for complex queries.

The queries so far has been very simple. But let us show an example demonstrating some of the power of SQL.

Create database structure:

CREATE TABLE customer (
    id INTEGER NOT NULL,
    name VARCHAR(32),
    address VARCHAR(64),
    PRIMARY KEY(id)
);
CREATE TABLE myorder (
    id INTEGER NOT NULL,
    customerid INTEGER,
    ordertime DATE,
    PRIMARY KEY(id)
);
CREATE TABLE orderline (
    id INTEGER NOT NULL,
    orderid INTEGER NOT NULL,
    item VARCHAR(32),
    itemprice NUMERIC(10,2),
    qty INTEGER,
    PRIMARY KEY(id)
);
INSERT INTO customer VALUES(1, 'A A', 'A Road 11');
INSERT INTO customer VALUES(2, 'B B', 'B Road 22');
INSERT INTO customer VALUES(3, 'C C', 'C Road 33');
INSERT INTO myorder VALUES(1, 1, '2021-04-04');
INSERT INTO myorder VALUES(2, 1, '2021-05-05');
INSERT INTO myorder VALUES(3, 1, '2021-06-06');
INSERT INTO myorder VALUES(4, 2, '2021-05-01');
INSERT INTO myorder VALUES(5, 3, '2021-06-01');
INSERT INTO orderline VALUES(1, 1, 'Something', 10.00, 1);
INSERT INTO orderline VALUES(2, 2, 'Something else', 20.00, 1);
INSERT INTO orderline VALUES(3, 3, 'Something different', 30.00, 1);
INSERT INTO orderline VALUES(4, 4, 'Something', 10.00, 1);
INSERT INTO orderline VALUES(5, 4, 'Something else', 20.00, 1);
INSERT INTO orderline VALUES(6, 5, 'Something different', 30.00, 3);

Note that the database structure is actually just a combination and extension of the first two examples!

Let us say that we want to show customers, their total sale and the time of the last sale.

Python:

import sqlite3

con = sqlite3.connect('order.db')
c = con.cursor()
c.execute('SELECT customer.name,SUM(orderline.itemprice*orderline.qty) AS sale,MAX(myorder.ordertime) AS lastorder\
           FROM (myorder JOIN orderline ON myorder.id = orderline.orderid) JOIN customer ON myorder.customerid = customer.id\
           GROUP BY customer.name\
           ORDER BY sale DESC;')
for row in c.fetchall():
    print('%s %10.2f %s' % (row[0], row[1], row[2]))
con.commit()
con.close()

I will not even try to create this example with index-sequential files and Cobol/Pascal. It can obviously be done, but it will be a lot more lines.

And this is non-trivial SQL but it is not complex SQL - it is possible to make much more complex queries in SQL.

Locking:

With index-sequential files operations like:

read record X
read record Y
...
update record X
update record Y

are not safe in multi-process (or multi-threaded) context.

It is necessary to do something like:

lock record X
lock record Y
read record X
read record Y
...
update record X
update record Y
unlock record X
unlock record Y

(VMS index-sequential files has excellent support for that)

The same problem exist for relational databases.
SELECT * FROM mytable WHERE id=X;
SELECT * FROM mytable WHERE id=Y;
...
UPDATE mytable SET somefield=somevalue WHERE id=X;
UPDATE mytable SET somefield=somevalue WHERE id=Y;

is not safe in multi-process or multi-threaded context.

But in a relational database one does not use explicit locking - one use transactions and and approriate transaction isolation level.

First ensure that transaction isolation level is serializable and then bundle the statements in a transaction.


BEGIN
SELECT * FROM mytable WHERE id=X;
SELECT * FROM mytable WHERE id=Y;
...
UPDATE mytable SET somefield=somevalue WHERE id=X;
UPDATE mytable SET somefield=somevalue WHERE id=Y;
COMMIT;

then the relational database will ensure proper synchronization.

For some databases that will mean that the database locks the involved rows similar to how one did it manually for index-sequential files.

Conclusion:

So what can we conclude from the above examples?

It is possible to migrate from index-sequential files to SQLite. No surprise.

It is possible to do data convertion by mapping both records in index-sequential files and tables in relational database to classes which allows to write the conversion programs in Java and Python instead of a traditional native language (Cobol, Pascal, Basic). Whether that is a real advantage depends on the available skills.

What to expect regarding code complexity by the change?
type of operation traditional native language ->
traditional native language
or Java with JDBC (*)
traditional native language ->
scripting language
or Java with JPA (*)
simple operations similar code less code
complex operations less code lot less code

*) Not counting Java getter and setter code that usually will be autogenerated.

Article history:

Version Date Description
1.0 July 5th 2021 Initial version

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj