实体管理器不会刷新所有实体
Entity Manager doesn't flush all entities
我的测试应用程序结构如下:
实体:
@Data
@Entity
@Table(name = "content")
public class Content {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String text;
}
存储库:
@Repository
public class ContentRepositoryCustomImpl implements ContentRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public void updateLastChar(int batchSize, String lastChar) {
TypedQuery<Long> countQuery = entityManager.createQuery("Select count(c.id) from Content c", Long.class);
long count = countQuery.getSingleResult();
TypedQuery<Content> query = entityManager.createQuery("Select c From Content c", Content.class);
int pageNumber = 1;
while (pageNumber < count) {
query.setFirstResult(pageNumber - 1);
query.setMaxResults(batchSize);
List<Content> contents = query.getResultList();
for (Content content : contents) {
String temp = content.getText().substring(1);
content.setText(temp + lastChar);
}
pageNumber += batchSize;
}
}
}
测试:
@ExtendWith(SpringExtension.class)
@SpringBootTest
class ContentRepositoryTest {
@Autowired
ContentRepository contentRepository;
@Test
@Transactional
@Commit
public void testOOMError2() throws InterruptedException {
contentRepository.updateLastChar(30, "5");
}
}
application.yml:
spring:
datasource:
username: user
password: pass
url: jdbc:postgresql://localhost:5432/pg?currentSchema=myschema&characterEncoding=UTF-8&serverTimezone=UTC
driverClassName: org.postgresql.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
maximumPoolSize: 20
jpa:
properties:
hibernate:
hbm2ddl:
auto: none
dialect: org.hibernate.dialect.PostgreSQL9Dialect
generate_statistics: true
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
DB:
CREATE TABLE etl.content
(
id integer NOT NULL DEFAULT nextval('etl.content_seq'::regclass),
text character varying(1048512) COLLATE pg_catalog."default",
CONSTRAINT content_pkey PRIMARY KEY (id)
)
在数据库中我有 100 条记录。 'text' 列每行已满(1048512 个字符)。
我在测试后得到的统计数据会不时改变。
所有记录更新时间:
Session Metrics {
1365887 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
9047519 nanoseconds spent preparing 104 JDBC statements;
10297157763 nanoseconds spent executing 104 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
875610249 nanoseconds spent executing 1 flushes (flushing a total of 100 entities and 0 collections);
8352008089 nanoseconds spent executing 5 partial-flushes (flushing a total of 180 entities and 180 collections)
当一些记录被更新时(在下面的例子中是 79):
Session Metrics {
1566421 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
8680213 nanoseconds spent preparing 104 JDBC statements;
10392348834 nanoseconds spent executing 104 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
735904121 nanoseconds spent executing 1 flushes (flushing a total of 79 entities and 0 collections);
8773113348 nanoseconds spent executing 5 partial-flushes (flushing a total of 158 entities and 158 collections)
我试图弄清楚为什么会这样。
当我将冲洗模式更改为 COMMIT 时,一切正常:
spring.jpa.properties.org.hibernate.flushMode=ALWAYS
不过,如果能了解问题的原因就好了。
更新 1:
我删除了
entityManager.persist(content);
正如 Simon Martinelli 所建议的那样,但它仍然不起作用(更新了 69 个实体而不是 100 个):
Session Metrics {
11661208 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
18982347 nanoseconds spent preparing 105 JDBC statements;
8964494681 nanoseconds spent executing 105 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
774310803 nanoseconds spent executing 1 flushes (flushing a total of 69 entities and 0 collections);
7321581493 nanoseconds spent executing 5 partial-flushes (flushing a total of 138 entities and 138 collections)
更新 2:
我添加了entityManager.flush():
public void updateLastChar(int batchSize, String lastChar) {
TypedQuery<Long> countQuery = entityManager.createQuery("Select count(c.id) from Content c", Long.class);
long count = countQuery.getSingleResult();
TypedQuery<Content> query = entityManager.createQuery("Select c From Content c", Content.class);
int pageNumber = 1;
while (pageNumber < count) {
query.setFirstResult(pageNumber - 1);
query.setMaxResults(batchSize);
List<Content> contents = query.getResultList();
for (Content content : contents) {
content.setText(lastChar);
}
entityManager.flush();
pageNumber += batchSize;
}
}
但是问题还是来了。而且,我的语句少于(65)条(这种行为也时有发生):
Session Metrics {
8190152 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
5825900 nanoseconds spent preparing 65 JDBC statements;
39315983 nanoseconds spent executing 65 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
57656774 nanoseconds spent executing 5 flushes (flushing a total of 249 entities and 0 collections);
3436917 nanoseconds spent executing 5 partial-flushes (flushing a total of 129 entities and 129 collections)
所以经过几个小时的痛苦,我终于找到了原因:
我不使用 ORDER BY 子句,这就是为什么 getResultList() 有时 returns 重复。
修复:
TypedQuery<Content> query = entityManager.createQuery("Select c From Content c ORDER BY c.id asc", Content.class);
我的测试应用程序结构如下:
实体:
@Data
@Entity
@Table(name = "content")
public class Content {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String text;
}
存储库:
@Repository
public class ContentRepositoryCustomImpl implements ContentRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public void updateLastChar(int batchSize, String lastChar) {
TypedQuery<Long> countQuery = entityManager.createQuery("Select count(c.id) from Content c", Long.class);
long count = countQuery.getSingleResult();
TypedQuery<Content> query = entityManager.createQuery("Select c From Content c", Content.class);
int pageNumber = 1;
while (pageNumber < count) {
query.setFirstResult(pageNumber - 1);
query.setMaxResults(batchSize);
List<Content> contents = query.getResultList();
for (Content content : contents) {
String temp = content.getText().substring(1);
content.setText(temp + lastChar);
}
pageNumber += batchSize;
}
}
}
测试:
@ExtendWith(SpringExtension.class)
@SpringBootTest
class ContentRepositoryTest {
@Autowired
ContentRepository contentRepository;
@Test
@Transactional
@Commit
public void testOOMError2() throws InterruptedException {
contentRepository.updateLastChar(30, "5");
}
}
application.yml:
spring:
datasource:
username: user
password: pass
url: jdbc:postgresql://localhost:5432/pg?currentSchema=myschema&characterEncoding=UTF-8&serverTimezone=UTC
driverClassName: org.postgresql.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
maximumPoolSize: 20
jpa:
properties:
hibernate:
hbm2ddl:
auto: none
dialect: org.hibernate.dialect.PostgreSQL9Dialect
generate_statistics: true
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
DB:
CREATE TABLE etl.content
(
id integer NOT NULL DEFAULT nextval('etl.content_seq'::regclass),
text character varying(1048512) COLLATE pg_catalog."default",
CONSTRAINT content_pkey PRIMARY KEY (id)
)
在数据库中我有 100 条记录。 'text' 列每行已满(1048512 个字符)。
我在测试后得到的统计数据会不时改变。
所有记录更新时间:
Session Metrics {
1365887 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
9047519 nanoseconds spent preparing 104 JDBC statements;
10297157763 nanoseconds spent executing 104 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
875610249 nanoseconds spent executing 1 flushes (flushing a total of 100 entities and 0 collections);
8352008089 nanoseconds spent executing 5 partial-flushes (flushing a total of 180 entities and 180 collections)
当一些记录被更新时(在下面的例子中是 79):
Session Metrics {
1566421 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
8680213 nanoseconds spent preparing 104 JDBC statements;
10392348834 nanoseconds spent executing 104 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
735904121 nanoseconds spent executing 1 flushes (flushing a total of 79 entities and 0 collections);
8773113348 nanoseconds spent executing 5 partial-flushes (flushing a total of 158 entities and 158 collections)
我试图弄清楚为什么会这样。 当我将冲洗模式更改为 COMMIT 时,一切正常:
spring.jpa.properties.org.hibernate.flushMode=ALWAYS
不过,如果能了解问题的原因就好了。
更新 1:
我删除了
entityManager.persist(content);
正如 Simon Martinelli 所建议的那样,但它仍然不起作用(更新了 69 个实体而不是 100 个):
Session Metrics {
11661208 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
18982347 nanoseconds spent preparing 105 JDBC statements;
8964494681 nanoseconds spent executing 105 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
774310803 nanoseconds spent executing 1 flushes (flushing a total of 69 entities and 0 collections);
7321581493 nanoseconds spent executing 5 partial-flushes (flushing a total of 138 entities and 138 collections)
更新 2:
我添加了entityManager.flush():
public void updateLastChar(int batchSize, String lastChar) {
TypedQuery<Long> countQuery = entityManager.createQuery("Select count(c.id) from Content c", Long.class);
long count = countQuery.getSingleResult();
TypedQuery<Content> query = entityManager.createQuery("Select c From Content c", Content.class);
int pageNumber = 1;
while (pageNumber < count) {
query.setFirstResult(pageNumber - 1);
query.setMaxResults(batchSize);
List<Content> contents = query.getResultList();
for (Content content : contents) {
content.setText(lastChar);
}
entityManager.flush();
pageNumber += batchSize;
}
}
但是问题还是来了。而且,我的语句少于(65)条(这种行为也时有发生):
Session Metrics {
8190152 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
5825900 nanoseconds spent preparing 65 JDBC statements;
39315983 nanoseconds spent executing 65 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
57656774 nanoseconds spent executing 5 flushes (flushing a total of 249 entities and 0 collections);
3436917 nanoseconds spent executing 5 partial-flushes (flushing a total of 129 entities and 129 collections)
所以经过几个小时的痛苦,我终于找到了原因: 我不使用 ORDER BY 子句,这就是为什么 getResultList() 有时 returns 重复。
修复:
TypedQuery<Content> query = entityManager.createQuery("Select c From Content c ORDER BY c.id asc", Content.class);