JPA 创建 Parent/child 与很多 children 的关系

JPA Create Parent/child relationship with a lot of children

我正在尝试使用 JPA 方法创建存储实体 Track 和 children 实体 TrackPoints。然而,存储 Track 及其 children TrackPoints 持续很长时间 - 大约 30 秒。我尝试了 GenerationType.IdentityGenerationType.SEQUENCE。如果我还有 Hibernate Spatial (Postgis) 列,它会持续更长时间——大约 60 秒来存储 parent 和所有 children。 JPA 按顺序发送插入,一个接着另一个。我该如何优化呢?谁能告诉我主要问题是什么?

技术:

Track.java

@Entity
@Table(name = "TRACKS")
public class Track implements Serializable {    
    @Id
    @Column(name = "track_id", nullable = false, unique = true)
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @NotNull
    @NotEmpty
    @Size(min = 1, max = 100)
    @Column(nullable = false, length = 100)
    private String name;

    @Size(max = 200)
    @Column(nullable = false, length = 200)
    private String description;

    @OneToOne(optional = false)
    @JoinColumn(name = "userId", nullable = false)
    private User userOwner;

    @NotNull
    @NotEmpty
    @Column(nullable = false, length = 55)
    private String type;

    @NotNull
    private Boolean isShared;

    @OneToMany(mappedBy = "track")
    private List<TrackPoint> trackPoints;
}

TrackPoint.java

@Entity
@Table(name = "TRACK_POINTS")
public class TrackPoint implements Serializable {

    private static final long serialVersionUID = 8089601593251025235L;

    @Id
    @Column(name = "trackpoint_id", nullable = false, unique = true)
    @GeneratedValue(generator = "track_point_sequence", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "track_point_sequence", sequenceName = "track_point_sequence", allocationSize = 1000)
    private Long id;

    @NotNull
    private int trackSegment;

    @NotNull
    private double elevation;

    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    private Date timeStamp;

    @NotNull
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name = "track_id")
    private Track track;

    /*Hibernate Spatial - Postgis field.
    @NotNull
    @Column(nullable = false)
    @Type(type = "org.hibernate.spatial.GeometryType")
    private Geometry location;*/
}

TrackService.java

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class TracksService implements ITracksService {

    @Inject
    private IDaoService dao;

    @Override
    public Long createTrack(GpxType gpx, String userId, String name, String desc) {
        // Map GPX to Track, TrackPoint object.
        track = dao.create(track);

    int batch_size = 50;

    int i = 0;

        for(TrackPoint point: track.getTrackPoints()) {
            dao.create(point);
            if(i++ % batch_size == 0) {
                dao.flush();
                dao.clear();
            }
        }

        return track.getId();

}

DaoService.java

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class DaoService implements IDaoService {

    @PersistenceContext()
    private EntityManager em;

    @Override
    public <T extends Serializable> T create(T t) {
        em.persist(t);
        return t;
    }
}

persistence.xml

<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xsi:schemaLocation=
                     "http://xmlns.jcp.org/xml/ns/persistence
                          http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="postgisTourbookPU" transaction-type="JTA">

        <description>PostgresSQL database with PostGIS extension</description>
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>${tourbook.datasource.postgresql.jndi-name}</jta-data-source>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <shared-cache-mode>NONE</shared-cache-mode>

        <properties>
            <!-- JPA properties -->
            <property name="javax.persistence.schema-generation.database.action"
                      value="drop-and-create"/>

            <!--            <property name="javax.persistence.schema-generation-target"
                                  value="database"/>-->

            <!-- Creation Schema Properties -->
            <property name="javax.persistence.schema-generation.create-source"
                      value="metadata"/>

            <!--            &lt;!&ndash; DDL Script location, when script is used &ndash;&gt;
                        <property name="javax.persistence.schema-generation.create-script-source"
                                    value="META-INF/create-script.sql"/>-->

            <!-- Drop Schema Properties -->
            <property name="javax.persistence.schema-generation.drop-source"
                      value="metadata"/>
            <!--            <property name="javax.persistence.schema-generation.drop-script-source"
                                  value="META-INF/drop-script.sql"/>-->

            <property name="javax.persistence.sql-load-script-source"
                                  value="META-INF/load-script.sql"/>

            <!-- JPA driver information -->
            <property name="javax.persistence.jdbc.driver"
                      value="org.postgresql.Driver"/>

            <!-- Hibernate properties -->
            <property name="hibernate.connection.characterEncoding"
                      value="UTF-8"/>

            <property name="hibernate.dialect"
                      value="org.hibernate.spatial.dialect.postgis.PostgisDialect"/>

            <property name="hibernate.default_schema"
                      value="public"/>

            <property name="hibernate.show_sql"
                      value="true"/>

            <property name="hibernate.jdbc.batch_size" value="50"/>

            <property name="hibernate.jdbc.fetch_size"
                      value="50"/>

            <property name="hibernate.order_inserts"
                      value="true"/>

            <property name="hibernate.order_updates"
                      value="true"/>

            <property name="hibernate.cache.use_query_cache"
                      value="false"/>

            <!-- Hibernate caching -->

        </properties>
    </persistence-unit>
</persistence>

已编辑

所以我试过了,在Hibernate中批量插入,但是我仍然需要30秒来保存2000点。

您正在插入包含所有子项的父项。在那种情况下,Hibernate JPA 确实会很慢,但是有一些技巧可以提高性能 - 查看休眠批处理指南 http://docs.jboss.org/hibernate/core/4.0/devguide/en-US/html/ch04.html - 我使用了 hibernate.jdbc.batch_size 参数(设置为例如 50)

祝你好运 加布里埃尔