INT-3029 JPA: Support Iterables for Persist/Merge
* Add test cases * Add documentation Jira: https://jira.springsource.org/browse/INT-3029 INT-3029 Update copyright year Doc Polishing
This commit is contained in:
committed by
Gary Russell
parent
772a01be79
commit
bbf93e1385
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.integration.jpa.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -22,6 +23,8 @@ import java.util.Set;
|
||||
import javax.persistence.Parameter;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.integration.jpa.support.JpaUtils;
|
||||
import org.springframework.integration.jpa.support.parametersource.ParameterSource;
|
||||
import org.springframework.integration.jpa.support.parametersource.PositionSupportingParameterSource;
|
||||
@@ -40,6 +43,8 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class DefaultJpaOperations extends AbstractJpaOperations {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultJpaOperations.class);
|
||||
|
||||
public void delete(Object entity) {
|
||||
Assert.notNull(entity, "The entity must not be null!");
|
||||
entityManager.remove(entity);
|
||||
@@ -172,11 +177,63 @@ public class DefaultJpaOperations extends AbstractJpaOperations {
|
||||
}
|
||||
|
||||
public Object merge(Object entity) {
|
||||
return entityManager.merge(entity);
|
||||
Assert.notNull(entity, "The object to merge must not be null.");
|
||||
return persistOrMerge(entity, true);
|
||||
}
|
||||
|
||||
public void persist(Object entity) {
|
||||
entityManager.persist(entity);
|
||||
Assert.notNull(entity, "The object to persist must not be null.");
|
||||
persistOrMerge(entity, false);
|
||||
}
|
||||
|
||||
private Object persistOrMerge(Object entity, boolean isMerge) {
|
||||
|
||||
if (entity instanceof Iterable) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterable<Object> entities = (Iterable<Object>) entity;
|
||||
|
||||
int savedEntities = 0;
|
||||
int nullEntities = 0;
|
||||
|
||||
List<Object> mergedEntities = new ArrayList<Object>();
|
||||
|
||||
for (Object iteratedEntity : entities) {
|
||||
if (iteratedEntity == null) {
|
||||
nullEntities++;
|
||||
}
|
||||
else {
|
||||
if (isMerge) {
|
||||
mergedEntities.add(entityManager.merge(iteratedEntity));
|
||||
}
|
||||
else {
|
||||
entityManager.persist(iteratedEntity);
|
||||
}
|
||||
savedEntities++;
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("%s %s entities. %s NULL entities were ignored.",
|
||||
isMerge ? "Merged" : "Persisted", savedEntities, nullEntities));
|
||||
}
|
||||
|
||||
if (isMerge) {
|
||||
return mergedEntities;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isMerge) {
|
||||
return entityManager.merge(entity);
|
||||
}
|
||||
else {
|
||||
entityManager.persist(entity);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,8 @@ package org.springframework.integration.jpa.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.springframework.integration.jpa.support.parametersource.ParameterSource;
|
||||
|
||||
/**
|
||||
@@ -138,7 +140,10 @@ public interface JpaOperations {
|
||||
Object getSingleResultForQuery(String query, ParameterSource source);
|
||||
|
||||
/**
|
||||
* The entity to be merged with the entity manager
|
||||
* The entity to be merged with the {@link EntityManager}. The provided object can
|
||||
* also be an {@link Iterable} in which case each object of the {@link Iterable}
|
||||
* is treated as an entity and merged with the {@link EntityManager}. {@code Null}
|
||||
* values returned while iterating over the {@link Iterable} are ignored.
|
||||
*
|
||||
* @param entity Must not be null.
|
||||
* @return The merged managed instance of the entity.
|
||||
@@ -146,7 +151,11 @@ public interface JpaOperations {
|
||||
Object merge(Object entity);
|
||||
|
||||
/**
|
||||
* Persists the entity
|
||||
* Persists the entity. The provided object can also be an {@link Iterable}
|
||||
* in which case each object of the {@link Iterable} is treated as an entity
|
||||
* and persisted with the {@link EntityManager}. {@code Null} values returned
|
||||
* while iterating over the {@link Iterable} are ignored.
|
||||
*
|
||||
* @param entity Must not be null
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.List;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.jpa.support.parametersource.ExpressionEvaluatingParameterSourceFactory;
|
||||
@@ -234,6 +233,92 @@ public class AbstractJpaOperationsTests {
|
||||
Assert.assertTrue(student != savedStudent);
|
||||
}
|
||||
|
||||
public void testMergeCollection() {
|
||||
final JpaOperations jpaOperations = getJpaOperations(entityManager);
|
||||
|
||||
final StudentDomain student1 = JpaTestUtils.getTestStudent();
|
||||
final StudentDomain student2 = JpaTestUtils.getTestStudent();
|
||||
final StudentDomain student3 = JpaTestUtils.getTestStudent();
|
||||
|
||||
student1.setFirstName("Karl");
|
||||
student2.setFirstName("Otto");
|
||||
student3.setFirstName("Wilhelm");
|
||||
|
||||
List<StudentDomain> students = new ArrayList<StudentDomain>(3);
|
||||
|
||||
students.add(student1);
|
||||
students.add(student2);
|
||||
students.add(student3);
|
||||
|
||||
Assert.assertNull(student1.getRollNumber());
|
||||
Assert.assertNull(student2.getRollNumber());
|
||||
Assert.assertNull(student3.getRollNumber());
|
||||
|
||||
Object savedStudents = jpaOperations.merge(students);
|
||||
entityManager.flush();
|
||||
|
||||
Assert.assertTrue(savedStudents instanceof List<?>);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<StudentDomain> savedStudentCollection = (List<StudentDomain>) savedStudents;
|
||||
|
||||
Assert.assertNotNull(savedStudentCollection.get(0).getRollNumber());
|
||||
Assert.assertNotNull(savedStudentCollection.get(1).getRollNumber());
|
||||
Assert.assertNotNull(savedStudentCollection.get(2).getRollNumber());
|
||||
}
|
||||
|
||||
public void testMergeNullCollection() {
|
||||
final JpaOperations jpaOperations = getJpaOperations(entityManager);
|
||||
|
||||
try {
|
||||
jpaOperations.merge(null);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
Assert.assertEquals("The object to merge must not be null.", e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.fail("Expected an IllegalArgumentException to be thrown.");
|
||||
|
||||
}
|
||||
|
||||
public void testMergeCollectionWithNullElement() {
|
||||
final JpaOperations jpaOperations = getJpaOperations(entityManager);
|
||||
|
||||
final List<?> studentsFromDbBeforeTest = jpaOperations.getResultListForClass(StudentDomain.class, 0);
|
||||
|
||||
Assert.assertEquals(3, studentsFromDbBeforeTest.size());
|
||||
|
||||
final StudentDomain student1 = JpaTestUtils.getTestStudent();
|
||||
final StudentDomain student2 = null;
|
||||
final StudentDomain student3 = JpaTestUtils.getTestStudent();
|
||||
|
||||
student1.setFirstName("Karl");
|
||||
student3.setFirstName("Wilhelm");
|
||||
|
||||
List<StudentDomain> students = new ArrayList<StudentDomain>(3);
|
||||
|
||||
students.add(student1);
|
||||
students.add(student2);
|
||||
students.add(student3);
|
||||
|
||||
Assert.assertNull(student1.getRollNumber());
|
||||
Assert.assertNull(student2);
|
||||
Assert.assertNull(student3.getRollNumber());
|
||||
|
||||
Object savedStudents = jpaOperations.merge(students);
|
||||
entityManager.flush();
|
||||
|
||||
Assert.assertTrue(savedStudents instanceof List<?>);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<StudentDomain> savedStudentCollection = (List<StudentDomain>) savedStudents;
|
||||
|
||||
Assert.assertNotNull(savedStudentCollection.get(0).getRollNumber());
|
||||
Assert.assertNotNull(savedStudentCollection.get(1).getRollNumber());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.integration.jpa.core.DefaultJpaOperations#persist(java.lang.Object)}.
|
||||
*/
|
||||
@@ -248,6 +333,83 @@ public class AbstractJpaOperationsTests {
|
||||
Assert.assertNotNull(student.getRollNumber());
|
||||
}
|
||||
|
||||
public void testPersistCollection() {
|
||||
final JpaOperations jpaOperations = getJpaOperations(entityManager);
|
||||
|
||||
final StudentDomain student1 = JpaTestUtils.getTestStudent();
|
||||
final StudentDomain student2 = JpaTestUtils.getTestStudent();
|
||||
final StudentDomain student3 = JpaTestUtils.getTestStudent();
|
||||
|
||||
student1.setFirstName("Karl");
|
||||
student2.setFirstName("Otto");
|
||||
student3.setFirstName("Wilhelm");
|
||||
|
||||
List<StudentDomain> students = new ArrayList<StudentDomain>(3);
|
||||
|
||||
students.add(student1);
|
||||
students.add(student2);
|
||||
students.add(student3);
|
||||
|
||||
Assert.assertNull(student1.getRollNumber());
|
||||
Assert.assertNull(student2.getRollNumber());
|
||||
Assert.assertNull(student3.getRollNumber());
|
||||
|
||||
jpaOperations.persist(students);
|
||||
entityManager.flush();
|
||||
Assert.assertNotNull(student1.getRollNumber());
|
||||
Assert.assertNotNull(student2.getRollNumber());
|
||||
Assert.assertNotNull(student3.getRollNumber());
|
||||
}
|
||||
|
||||
public void testPersistNullCollection() {
|
||||
final JpaOperations jpaOperations = getJpaOperations(entityManager);
|
||||
|
||||
try {
|
||||
jpaOperations.persist(null);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
Assert.assertEquals("The object to persist must not be null.", e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.fail("Expected an IllegalArgumentException to be thrown.");
|
||||
|
||||
}
|
||||
|
||||
public void testPersistCollectionWithNullElement() {
|
||||
final JpaOperations jpaOperations = getJpaOperations(entityManager);
|
||||
|
||||
final List<?> studentsFromDbBeforeTest = jpaOperations.getResultListForClass(StudentDomain.class, 0);
|
||||
|
||||
Assert.assertEquals(3, studentsFromDbBeforeTest.size());
|
||||
|
||||
final StudentDomain student1 = JpaTestUtils.getTestStudent();
|
||||
final StudentDomain student2 = null;
|
||||
final StudentDomain student3 = JpaTestUtils.getTestStudent();
|
||||
|
||||
student1.setFirstName("Karl");
|
||||
student3.setFirstName("Wilhelm");
|
||||
|
||||
List<StudentDomain> students = new ArrayList<StudentDomain>(3);
|
||||
|
||||
students.add(student1);
|
||||
students.add(student2);
|
||||
students.add(student3);
|
||||
|
||||
Assert.assertNull(student1.getRollNumber());
|
||||
Assert.assertNull(student2);
|
||||
Assert.assertNull(student3.getRollNumber());
|
||||
|
||||
jpaOperations.persist(students);
|
||||
entityManager.flush();
|
||||
Assert.assertNotNull(student1.getRollNumber());
|
||||
Assert.assertNotNull(student3.getRollNumber());
|
||||
|
||||
final List<?> studentsFromDb = jpaOperations.getResultListForClass(StudentDomain.class, 0);
|
||||
|
||||
Assert.assertNotNull(studentsFromDb);
|
||||
Assert.assertEquals(5, studentsFromDb.size());
|
||||
}
|
||||
|
||||
public void testDeleteInBatch() {
|
||||
final JpaOperations jpaOperations = getJpaOperations(entityManager);
|
||||
|
||||
@@ -15,7 +15,6 @@ package org.springframework.integration.jpa.core;
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -99,12 +98,48 @@ public class EclipseLinkJpaOperationsTests extends AbstractJpaOperationsTests {
|
||||
super.testMerge();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testMergeCollection() {
|
||||
super.testMergeCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testMergeNullCollection() {
|
||||
super.testMergeNullCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testMergeCollectionWithNullElement() {
|
||||
super.testMergeCollectionWithNullElement();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersist() {
|
||||
super.testPersist();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersistCollection() {
|
||||
super.testPersistCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersistNullCollection() {
|
||||
super.testPersistNullCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersistCollectionWithNullElement() {
|
||||
super.testPersistCollectionWithNullElement();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testGetAllStudents() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
@@ -128,12 +128,48 @@ public class HibernateJpaOperationsTests extends AbstractJpaOperationsTests {
|
||||
super.testMerge();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testMergeCollection() {
|
||||
super.testMergeCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testMergeNullCollection() {
|
||||
super.testMergeNullCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testMergeCollectionWithNullElement() {
|
||||
super.testMergeCollectionWithNullElement();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersist() {
|
||||
super.testPersist();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersistCollection() {
|
||||
super.testPersistCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersistNullCollection() {
|
||||
super.testPersistNullCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersistCollectionWithNullElement() {
|
||||
super.testPersistCollectionWithNullElement();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testDeleteInBatch() {
|
||||
|
||||
@@ -17,7 +17,6 @@ import java.sql.SQLException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
||||
import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
|
||||
import org.apache.openjpa.jdbc.meta.MappingTool;
|
||||
@@ -144,12 +143,48 @@ public class OpenJpaJpaOperationsTests extends AbstractJpaOperationsTests {
|
||||
super.testMerge();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testMergeCollection() {
|
||||
super.testMergeCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testMergeNullCollection() {
|
||||
super.testMergeNullCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testMergeCollectionWithNullElement() {
|
||||
super.testMergeCollectionWithNullElement();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersist() {
|
||||
super.testPersist();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersistCollection() {
|
||||
super.testPersistCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersistNullCollection() {
|
||||
super.testPersistNullCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testPersistCollectionWithNullElement() {
|
||||
super.testPersistCollectionWithNullElement();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testGetAllStudents() {
|
||||
|
||||
@@ -634,6 +634,19 @@
|
||||
them to <emphasis>PERSIST</emphasis>,<emphasis>MERGE</emphasis> or <emphasis>DELETE</emphasis>
|
||||
it from the underlying data source.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
As of <emphasis>Spring Integration 3.0</emphasis>,
|
||||
payloads to <emphasis>persist</emphasis> or
|
||||
<emphasis>merge</emphasis> can also be of type
|
||||
<interfacename><ulink url="http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html">java.lang.Iterable</ulink></interfacename>.
|
||||
In that case, each object returned by the
|
||||
<interfacename>Iterable</interfacename> is treated as
|
||||
an entity and persisted or merged using the underlying
|
||||
<interfacename>EntityManager</interfacename>.
|
||||
<emphasis>NULL</emphasis> values returned by the iterator are ignored.
|
||||
</para>
|
||||
</note>
|
||||
</section>
|
||||
<section>
|
||||
<title>Using JPA Query Language (JPA QL)</title>
|
||||
@@ -893,10 +906,23 @@ public class Student {
|
||||
</callout>
|
||||
<callout arearefs="outAdaptPersistMode">
|
||||
<para>
|
||||
Accepts one of the <emphasis>PERSIST</emphasis>,<emphasis>MERGE</emphasis> or <emphasis>DELETE</emphasis>. Indicates the operation that the adapter needs to perform.
|
||||
Accepts one of the following: <emphasis>PERSIST</emphasis>,
|
||||
<emphasis>MERGE</emphasis> or <emphasis>DELETE</emphasis>.
|
||||
Indicates the operation that the adapter needs to perform.
|
||||
Relevant only if an entity is being used for JPA operations.
|
||||
Ignored if JPA QL, named query or native query is provided. Defaults to <emphasis>MERGE</emphasis>. <emphasis>Optional</emphasis>.
|
||||
</para>
|
||||
<para>
|
||||
As of <emphasis role="bold">Spring Integration 3.0</emphasis>,
|
||||
payloads to <emphasis>persist</emphasis> or
|
||||
<emphasis>merge</emphasis> can also be of type
|
||||
<interfacename><ulink url="http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html">java.lang.Iterable</ulink></interfacename>.
|
||||
In that case, each object returned by the
|
||||
<interfacename>Iterable</interfacename> is treated as
|
||||
an entity and persisted or merged using the underlying
|
||||
<interfacename>EntityManager</interfacename>.
|
||||
<emphasis>NULL</emphasis> values returned by the iterator are ignored.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="outAdaptUserPayloadAsParamSrc">
|
||||
<para>
|
||||
|
||||
@@ -194,6 +194,21 @@
|
||||
please see <xref linkend="jdbc-message-store-generic"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jpa-persist-merge-collections">
|
||||
<title>JPA Support Improvements</title>
|
||||
<para>
|
||||
Payloads to <emphasis>persist</emphasis> or
|
||||
<emphasis>merge</emphasis> can now be of type
|
||||
<interfacename><ulink url="http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html">java.lang.Iterable</ulink></interfacename>.
|
||||
</para>
|
||||
<para>
|
||||
In that case, each object returned by the
|
||||
<interfacename>Iterable</interfacename> is treated as
|
||||
an entity and persisted or merged using the underlying
|
||||
<interfacename>EntityManager</interfacename>.
|
||||
<emphasis>NULL</emphasis> values returned by the iterator are ignored.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-json-transformers">
|
||||
<title>Jackson Support (JSON)</title>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user