JPA Создать родительско-дочерние отношения с большим количеством дочерних элементов

Я пытаюсь сохранить объект Track с дочерними объектами TrackPoints с помощью метода JPA create. Однако сохранение Track с его дочерними элементами TrackPoints длится очень долго — около 30 секунд. Я попробовал GenerationType.Identity и GenerationType.SEQUENCE. Если у меня также есть столбец Hibernate Spatial (Postgis), он длится еще дольше — около 60 секунд для хранения родительского и всех дочерних элементов. JPA отправляет вставку последовательно одну за другой. Как я могу оптимизировать это? Может кто подскажет в чем основная проблема?

Технологии:

  • Wildfly 8.1, JPA 2.1 (спящий режим), Hibernate Spatial, EJB, JTA
  • PostgreSQL 9.3 + PostGis — установка по умолчанию (просто установите из пакета Ubuntu)

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 баллов.


person vkasala    schedule 13.02.2015    source источник
comment
Вы храните все в одной транзакции? Также вы можете использовать SequenceGenerator, который не запрашивает у базы данных новый идентификатор каждый раз, когда вам нужен новый.   -  person Kikaimaru    schedule 14.02.2015
comment
Привет, я делаю все в одной транзакции. Это проблема? Я также пробовал пакет, но это не помогло.   -  person vkasala    schedule 15.02.2015
comment
Привет, я наконец-то разобрался. Я включил регистратор на org.hibernate в Wildfly. Спасибо за вашу помощь.   -  person vkasala    schedule 17.02.2015


Ответы (1)


вы вставляете родителя со всеми детьми. В этом случае Hibernate JPA действительно может быть медленным, но есть несколько советов по повышению производительности — обратитесь к руководству по пакетному режиму hibernate http://docs.jboss.org/hibernate/core/4.0/devguide/en-US/html/ch04.html — я мы использовали параметр hibernate.jdbc.batch_size (установите, например, 50)

Удачи, Габриэль

person gusto2    schedule 13.02.2015
comment
Привет, Габриэль. Я пробовал пакетную вставку, но если повезет. Теперь на 2000 TrackPoints уходит около 30-35 секунд. Я прикрепил измененный код. - person vkasala; 15.02.2015