1

I'm trying to map three table columns in three primitive arrays. But whatever I try, I can't get this to work. Here's the code:

class Parent {
    private Component component;
}

class Component {
    private Parent parent;
    private int[] valuesOne;
    private int[] valuesTwo;
    private double[] valuesThree;
}

Hibernate mapping:

<class name="com.package.Parent" table="parent">
    <id name="id" column="id" access="field" type="int">
        <generator class="increment"/>
    </id>
    ...
    <component name="component" access="field">
        <many-to-one name="parent" class="com.package.Parent" insert="false" update="false" fetch="join" column="id" access="field"/>
        <primitive-array name="valuesOne" table="component" access="field">
            <key column="parent_id"/>
            <index column="index"/>
            <element column="value_one" type="int"/>
        </primitive-array>
        <primitive-array name="valuesTwo" table="component" access="field">
            <key column="parent_id"/>
            <index column="index"/>
            <element column="value_two" type="int"/>
        </primitive-array>
        <primitive-array name="valuesThree" table="component" access="field">
            <key column="parent_id"/>
            <index column="index"/>
            <element column="value_three" type="double"/>
        </primitive-array>
    </component>
</class>

Database table:

CREATE TABLE parent (
    id              INTEGER NOT NULL AUTO_INCREMENT,
    ...
)


CREATE TABLE component (
    id              INTEGER NOT NULL AUTO_INCREMENT,
    parent_id       INTEGER NOT NULL,
    index           INTEGER NOT NULL,
    value_one       INTEGER NOT NULL,
    value_two       INTEGER NOT NULL,
    value_three     DECIMAL NOT NULL
)

Now this half works. Hibernate will create for each value to store a separate query, like this:

Hibernate: 
    insert 
    into
        component
        (parent_id, index, value_one) 
    values
        (?, ?, ?)

What I actually want is:

Hibernate: 
    insert 
    into
        component
        (parent_id, index, value_one, value_two, value_three) 
    values
        (?, ?, ?, ?, ?)

How can I achieve this through Hibernate mapping?

Displee
  • 670
  • 8
  • 20
  • 3
    I don't get it. In Java you have array of integers. But your CREATE TABLE has single integer. How exactly do you plan to map array into a single column? You should probably have multiple tables, one per array. Alternatively, you can store array of integers as a BLOB or something. – Tarlog Apr 01 '20 at 04:14
  • You're partially correct. I was just overthinking this I guess. I just needed to create an extra class and map that as an array inside the `Component` class. – Displee Apr 14 '20 at 15:33

3 Answers3

0

Try this solution: In order to implement bulk and batch inserts then need to configure the batch size in application.properties:

spring.jpa.properties.hibernate.jdbc.batch_size=5
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

Then you need to create an entity (model) for Component table

@Entity
@Table(name="Component")
public class Component {
@Id
private String id;

@Column(name="value_one")
private int value_one;

@Column(name="value_two")
private int value_two;

@Column(name="value_three")
private double value_three;

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public int getValue_one() {
    return value_one;
}

public void setValue_one(int value_one) {
    this.value_one = value_one;
}

public int getValue_two() {
    return value_two;
}

public void setValue_two(int value_two) {
    this.value_two = value_two;
}

public double getValue_three() {
    return value_three;
}

public void setValue_three(double value_three) {
    this.value_three = value_three;
}

}

Repository interface with the database is defined below:

@Repository
public interface ComponentRepository  
    extends JpaRepository<Component, Integer> {
}

The business logic is implemented in the service class:

@Service
 public class ComponentService {
@Resource
private ComponentRepository compRepository;

@Transactional // this annotaion is
// important for transaction management in the database
public void saveComponents(List<Component> components) {
    int size = components.size();
    int counter = 0;
    List<Component> temp = new ArrayList<>();
    for (Component emp : components) {
        temp.add(emp);
        if ((counter + 1) % 100 == 0
                || (counter + 1) == size) {
            compRepository.saveAll(temp);
            temp.clear();
        }
        counter++;

    }
}
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
0

You are definitely using <primitive-array> in the wrong way. Suppose you have an entity called Player with the uniformNumbers as a property. And you have defined the uniformNumbers property as an int array. In order to use <primitive-array> correctly, you will need to define a table for the Player entity and an associative table for the Uniform.

public class Player {
    private int[] uniformNumbers;
}

A <primitive-array> element in your mapping allows you to map an array of primitives to an associated table in the database. Player instances are stored in one table, while the associated array of primitives, in this case, uniformNumbers, is stored in another table. Each primitive in the array gets its own row in the associated Uniform table. Each row is identified by the primitive value and the id of the owning entity, in this case, Player's id. However, when a Player is obtained from the database, the entire collection of rows from Uniform that are associated to the Player (by ID) are used to obtain the array of ints for the uniformNumbers property.

<class name="Player" table="player">
    <id name="id" column="id">
        <generator class="increment"/>
    </id>
    ...
    <primitive-array name="uniformNumbers" table="uniform">
        <key column="player_id"/>
        <index column="order"/>
        <element column="uniform_number" type="int"/>
    </primitive-array>
</class>

In other words, you will have to define three associated tables. One for each primitive array.

Felipe Desiderati
  • 2,414
  • 3
  • 24
  • 42
0

Apparently I was overthinking this, I just needed to create one array with multiple fields.

class Component {
    private Parent parent;
    private ComponentEntry[] entries;
}

class ComponentEntry {
    private int id;
    private Component parent;
    private int valueOne;
    private int valueTwo;
    private double valueThree;
}

Then just map the array.

<array name="entries" table="component" cascade="all-delete-orphan" access="field">
    <key column="parent_id" not-null="true"/>
    <index column="index" type="int"/>
    <one-to-many class="ComponentEntry"/>
</array>
Displee
  • 670
  • 8
  • 20