[
https://issues.apache.org/jira/browse/OPENJPA-3019?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18130896#comment-18130896
]
ASF subversion and git services commented on OPENJPA-3019:
----------------------------------------------------------
Commit 4dd1fd34ba85d480c5cf75b68e45473ad78696e7 in openjpa's branch
refs/heads/OPENJPA-3019 from Richard Zowalla
[ https://gitbox.apache.org/repos/asf?p=openjpa.git;h=4dd1fd34b ]
[OPENJPA-3019] Write an in-place change to an element of a collection converted
as a whole
The proxy of such a collection only sees adds and removes, so a change made
inside an element was never written. The converted value of these fields is
now saved when they are loaded, and compared once before each flush; a field
whose converted value differs is marked dirty.
> In-place change to an element of a collection converted as a whole is not
> written
> ---------------------------------------------------------------------------------
>
> Key: OPENJPA-3019
> URL: https://issues.apache.org/jira/browse/OPENJPA-3019
> Project: OpenJPA
> Issue Type: Bug
> Reporter: Richard Zowalla
> Assignee: Richard Zowalla
> Priority: Major
>
> A change made in place to an element of a collection that is converted as a
> whole is not written to the database. Given
> {code:java}
> @Convert(converter = ItemListConverter.class) //
> AttributeConverter<List<Item>, String>
> private List<Item> items = new ArrayList<>();
> {code}
> the following commits without an {{UPDATE}}, and the change is lost:
> {code:java}
> em.getTransaction().begin();
> em.find(InPlaceConvertedEntity.class,
> 1L).getItems().get(0).setName("changed");
> em.getTransaction().commit();
> {code}
> Adding or removing an element is written. So is a change made in place to a
> converted attribute that is not a collection, e.g. {{@Convert(converter =
> ItemConverter.class) private Item item}} followed by
> {{getItem().setName("changed")}}. Only the collection case is affected. The
> collection is wrapped in a proxy that tracks adds and removes, and the field
> is only marked dirty through that proxy, so a change inside an element is
> never seen.
> The specification requires the change to be written. Section "Type Conversion
> of Basic Attributes": "A converted attribute is considered a basic
> attribute". Section "Synchronization to the Database": "A modification to the
> state of an entity might include an assignment of a new value to a persistent
> property or field of the entity, or even modification of a mutable value of a
> persistent property or field."
> Mapping a collection converted as a whole only works with OPENJPA-3017 (PR
> #220). Reported there by Francesco Chicchiriccò while testing Apache Syncope
> against 4.2.0-SNAPSHOT: changing a value of a {{PlainAttr}} held in a
> converted {{List<PlainAttr>}} and merging the user produced no {{UPDATE}},
> while removing the attribute, changing it and adding it back did.
> Test case, on top of OPENJPA-3017, package
> {{org.apache.openjpa.persistence.conversion}}:
> {code:java}
> @Converter
> public class ItemConverter implements AttributeConverter<Item, String> {
> public String convertToDatabaseColumn(Item attribute) {
> return attribute == null ? null : attribute.getName();
> }
> public Item convertToEntityAttribute(String dbData) {
> return dbData == null ? null : new Item(dbData);
> }
> }
> @Entity
> public class InPlaceConvertedEntity {
> @Id
> private long id;
> @Convert(converter = ItemListConverter.class)
> private List<Item> items = new ArrayList<>();
> @Convert(converter = ItemConverter.class)
> private Item item;
> // getters and setters
> }
> public class TestConvertedInPlaceChange extends SingleEMFTestCase {
> public void setUp() {
> setUp(InPlaceConvertedEntity.class, CLEAR_TABLES);
> try (EntityManager em = emf.createEntityManager()) {
> em.getTransaction().begin();
> InPlaceConvertedEntity e = new InPlaceConvertedEntity();
> e.setId(1L);
> e.getItems().add(new Item("one"));
> e.getItems().add(new Item("two"));
> e.setItem(new Item("single"));
> em.persist(e);
> em.getTransaction().commit();
> }
> }
> // fails: expected:<[changed]> but was:<[one]>
> public void testElementOfConvertedListChangedInPlace() {
> try (EntityManager em = emf.createEntityManager()) {
> em.getTransaction().begin();
> em.find(InPlaceConvertedEntity.class,
> 1L).getItems().get(0).setName("changed");
> em.getTransaction().commit();
> }
> try (EntityManager em = emf.createEntityManager()) {
> assertEquals("changed",
> em.find(InPlaceConvertedEntity.class,
> 1L).getItems().get(0).getName());
> }
> }
> // passes
> public void testConvertedValueChangedInPlace() {
> try (EntityManager em = emf.createEntityManager()) {
> em.getTransaction().begin();
> em.find(InPlaceConvertedEntity.class,
> 1L).getItem().setName("changed");
> em.getTransaction().commit();
> }
> try (EntityManager em = emf.createEntityManager()) {
> assertEquals("changed", em.find(InPlaceConvertedEntity.class,
> 1L).getItem().getName());
> }
> }
> // passes
> public void testConvertedListChangedStructurally() {
> try (EntityManager em = emf.createEntityManager()) {
> em.getTransaction().begin();
> em.find(InPlaceConvertedEntity.class, 1L).getItems().add(new
> Item("three"));
> em.getTransaction().commit();
> }
> try (EntityManager em = emf.createEntityManager()) {
> assertEquals(3, em.find(InPlaceConvertedEntity.class,
> 1L).getItems().size());
> }
> }
> }
> {code}
> {{Item}} is a plain {{Serializable}} bean with a {{name}} property, and
> {{ItemListConverter}} joins the names with commas, as in the tests of
> OPENJPA-3017.
> Possible fix: at flush, compare the converted database value of such a field
> with the value it had when it was loaded, and mark the field dirty when they
> differ. Alternatively, do not proxy a collection that is converted as a whole
> and treat it like the non-collection case.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)