JPA 创建 Parent/child 与很多 children 的关系
JPA Create Parent/child relationship with a lot of children
我正在尝试使用 JPA 方法创建存储实体 Track 和 children 实体 TrackPoints。然而,存储 Track 及其 children TrackPoints 持续很长时间 - 大约 30 秒。我尝试了 GenerationType.Identity 和 GenerationType.SEQUENCE。如果我还有 Hibernate Spatial (Postgis) 列,它会持续更长时间——大约 60 秒来存储 parent 和所有 children。 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"/>
<!-- <!– DDL Script location, when script is used –>
<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)
祝你好运
加布里埃尔
我正在尝试使用 JPA 方法创建存储实体 Track 和 children 实体 TrackPoints。然而,存储 Track 及其 children TrackPoints 持续很长时间 - 大约 30 秒。我尝试了 GenerationType.Identity 和 GenerationType.SEQUENCE。如果我还有 Hibernate Spatial (Postgis) 列,它会持续更长时间——大约 60 秒来存储 parent 和所有 children。 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"/>
<!-- <!– DDL Script location, when script is used –>
<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)
祝你好运 加布里埃尔