/* * Copyright (C) 2015 Red Hat, Inc. and/or its affiliates. * * 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.errai.jpa.test.client; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContextType; import javax.persistence.PersistenceException; import javax.persistence.PostLoad; import javax.persistence.PostPersist; import javax.persistence.PostRemove; import javax.persistence.PostUpdate; import javax.persistence.PrePersist; import javax.persistence.PreRemove; import javax.persistence.PreUpdate; import org.jboss.errai.common.client.api.WrappedPortable; import org.jboss.errai.databinding.client.api.DataBinder; import org.jboss.errai.databinding.client.api.handler.property.PropertyChangeEvent; import org.jboss.errai.databinding.client.api.handler.property.PropertyChangeHandler; import org.jboss.errai.ioc.client.Container; import org.jboss.errai.ioc.client.container.IOC; import org.jboss.errai.jpa.client.local.ErraiEntityManager; import org.jboss.errai.jpa.client.local.ErraiMetamodel; import org.jboss.errai.jpa.client.local.Key; import org.jboss.errai.jpa.client.local.backend.LocalStorage; import org.jboss.errai.jpa.rebind.ErraiEntityManagerGenerator; import org.jboss.errai.jpa.test.client.res.JpaClientTestCase; import org.jboss.errai.jpa.test.entity.Album; import org.jboss.errai.jpa.test.entity.Artist; import org.jboss.errai.jpa.test.entity.CallbackLogEntry; import org.jboss.errai.jpa.test.entity.CascadeFrom; import org.jboss.errai.jpa.test.entity.Format; import org.jboss.errai.jpa.test.entity.Genre; import org.jboss.errai.jpa.test.entity.MethodAccessedZentity; import org.jboss.errai.jpa.test.entity.StandaloneLifecycleListener; import org.jboss.errai.jpa.test.entity.Zentity; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.user.client.ui.TextBox; /** * Tests the JPA EntityManager facilities provided by Errai JPA. * <p> * Note that there is a {@link HibernateJpaTest subclass of this test} that runs * all the same checks against Hibernate, as a sanity check that we're testing * for actual JPA-sanctioned and JPA-compatible behaviour. * * @author Jonathan Fuerth <jfuerth@gmail.com> */ public class ErraiJpaTest extends JpaClientTestCase { @Override public String getModuleName() { return "org.jboss.errai.jpa.test.JpaTest"; } protected EntityManager getEntityManager() { final JpaTestClient testClient = JpaTestClient.INSTANCE; assertNotNull(testClient); assertNotNull(testClient.entityManager); ((ErraiEntityManager) testClient.entityManager).removeAll(); return testClient.entityManager; } @Override protected void gwtSetUp() throws Exception { super.gwtSetUp(); Album.CALLBACK_LOG.clear(); // We need to bootstrap the IoC container manually because GWTTestCase // doesn't call onModuleLoad() for us. new Container().bootstrapContainer(); } @Override protected void gwtTearDown() throws Exception { Container.reset(); IOC.reset(); } /** * Tests that the entity manager was injected into the testing class. If this * test fails, the likely cause is that the * {@link ErraiEntityManagerGenerator} failed to output a compilable class. In * that case, try re-running this test with * {@code -Derrai.codegen.permissive=true} and */ public void testEntityManagerInjection() throws Exception { getEntityManager(); // has its own assertions } /** * Tests the rejection of a non-entity type. */ public void testPersistNonEntity() { try { final EntityManager em = getEntityManager(); em.persist("this is a string, not an entity"); fail(); } catch (final IllegalArgumentException ex) { // this is the behaviour we are testing for } } /** * Regression check for ERRAI-675. */ public void testNonClientEntityIsNotInEntityManager() { try { // we cannot use the class name to test here since the class is not available in client side code generation ((ErraiMetamodel)getEntityManager().getMetamodel()).entity("org.jboss.errai.jpa.test.not.on.gwt.path" + ".NonClientEntity"); // it's actually more likely that the whole code generation thing fails fail("NonClientEntity was included"); } catch (final IllegalArgumentException ex) { // this is the behaviour we are testing for } } /** * Tests the persistence of one entity with no related entities. */ public void testPersistOneAlbum() { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); em.detach(album); assertNotNull(album.getId()); // fetch it final Album fetchedAlbum = em.find(Album.class, album.getId()); assertNotSame(album, fetchedAlbum); assertEquals(album.toString(), fetchedAlbum.toString()); } /** * Tests the persistence of two unrelated entities of different types. */ public void testPersistOneAlbumAndOneArtist() { // make Album (not attached to Artist) final Album album = new Album(); album.setArtist(null); album.setName("Let It Be"); album.setReleaseDate(new Date(11012400000L)); // store them final EntityManager em = getEntityManager(); em.persist(album); em.flush(); // make Artist (completely unrelated to Album, but has same numeric ID) final Artist artist = new Artist(); artist.setId(album.getId()); // to verify proper separation by entity type artist.setName("The Beatles"); em.persist(artist); em.flush(); em.detach(album); em.detach(artist); // fetch them final Album fetchedAlbum = em.find(Album.class, album.getId()); final Artist fetchedArtist = em.find(Artist.class, artist.getId()); assertNotSame(album, fetchedAlbum); assertNotSame(artist, fetchedArtist); // ensure Album is intact assertEquals(album.toString(), fetchedAlbum.toString()); // ensure Artist is intact assertEquals(artist.toString(), fetchedArtist.toString()); } /** * Tests that an entity that was just persisted in this session is always a * canonical reference to the same object. */ public void testRetrievePersistedEntity() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); // should come directly from the persistence unit cache at this point Album fetchedAlbum = em.find(Album.class, album.getId()); assertSame(album, fetchedAlbum); // ensure it's stored in the database em.flush(); // should still come directly from the persistence unit cache fetchedAlbum = em.find(Album.class, album.getId()); assertSame(album, fetchedAlbum); } /** * Tests that an entity that was fetched in this session is always a canonical * reference to the same object. */ public void testRetrieveEntityTwice() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); // ensure it's stored in the database em.flush(); // now forget it em.detach(album); // multiple fetches should come directly from the persistence unit cache at this point final Album fetchedAlbum = em.find(Album.class, album.getId()); final Album fetchedAlbum2 = em.find(Album.class, album.getId()); assertSame(fetchedAlbum, fetchedAlbum2); // ensure it's not the original instance we persisted and detached assertNotSame(album, fetchedAlbum); } /** * Tests the persistence of two unrelated entities of different types. */ public void testClearDetachesAll() { // make Album final Album album = new Album(); album.setArtist(null); album.setName("Let It Be"); album.setReleaseDate(new Date(11012400000L)); // make artist final Artist artist = new Artist(); artist.setId(123L); artist.setName("The Beatles"); // store them final EntityManager em = getEntityManager(); em.persist(album); em.persist(artist); em.flush(); // make sure they're persistent and managed assertSame(album, em.find(Album.class, album.getId())); assertSame(artist, em.find(Artist.class, artist.getId())); em.clear(); // make sure they were detached final Album fetchedAlbum = em.find(Album.class, album.getId()); final Artist fetchedArtist = em.find(Artist.class, artist.getId()); assertNotNull(fetchedAlbum); assertNotNull(fetchedArtist); assertNotSame(album, fetchedAlbum); assertNotSame(artist, fetchedArtist); } public void testRemoveOneEntity() { // make Album final Album album = new Album(); album.setArtist(null); album.setName("Let It Be"); album.setReleaseDate(new Date(11012400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); // make sure it's persistent and managed assertSame(album, em.find(Album.class, album.getId())); // remove it em.remove(album); // make sure it's gone assertNotNull(album.getId()); assertFalse(em.contains(album)); assertNull(em.find(Album.class, album.getId())); } public void testUpdateEntity() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); // modify it album.setName("Cowabunga"); em.flush(); // fetch and compare em.clear(); final Album fetchedAlbum = em.find(Album.class, album.getId()); assertNotNull(fetchedAlbum); assertNotSame(album, fetchedAlbum); assertEquals(album.toString(), fetchedAlbum.toString()); } public void testIdUpdateIsRejected() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); // set ID (not allowed) try { album.setId(1234L); em.flush(); fail("ID change was not detected"); } catch (final PersistenceException e) { assertTrue( e.getMessage().contains("Actual ID: 1234") || // errai message e.getMessage().contains("to 1234")); // hibernate message } } public void testPersistRelatedCollection() { // make them final Artist artist = new Artist(); artist.setId(9L); // Artist uses user-assigned/non-generated IDs artist.setName("The Beatles"); final Album album = new Album(); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); album.setArtist(artist); artist.addAlbum(album); // store it final EntityManager em = getEntityManager(); em.persist(artist); // should cascade onto album, which is in the collection relation em.flush(); assertNotNull(album.getId()); // ensure both are in the persistence context assertSame(artist, em.find(Artist.class, artist.getId())); assertSame(album, em.find(Album.class, album.getId())); em.clear(); // ensure both are retrieved (TBD: should Errai always/ever fetch related entities?) final Artist fetchedArtist = em.find(Artist.class, artist.getId()); assertNotNull(fetchedArtist); assertEquals(1, fetchedArtist.getAlbums().size()); final Album cascadeFetchedAlbum = fetchedArtist.getAlbums().iterator().next(); assertNotNull(cascadeFetchedAlbum); assertNotSame(artist, fetchedArtist); assertNotSame(album, cascadeFetchedAlbum); assertEquals(artist.toString(), fetchedArtist.toString()); assertEquals(album.toString(), cascadeFetchedAlbum.toString()); // now ensure we haven't retrieved a ghost album assertSame(cascadeFetchedAlbum, em.find(Album.class, cascadeFetchedAlbum.getId())); // ensure "parent pointer" of album points at correct artist instance assertSame(fetchedArtist, cascadeFetchedAlbum.getArtist()); } public void testPersistNullOneToMany() { // artist is the container final Artist artist = new Artist(); artist.setId(98L); // Artist uses user-assigned/non-generated IDs artist.setName("The Beatles"); // this one has the null artist final Album album = new Album(); album.setName("Mystery Album"); album.setArtist(null); album.setReleaseDate(new Date(-9366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(artist); em.persist(album); em.flush(); // sanity check assertNotNull(album.getId()); // ensure everything's in the persistence context assertSame(artist, em.find(Artist.class, artist.getId())); assertSame(album, em.find(Album.class, album.getId())); em.clear(); final Album fetchedAlbum2 = em.find(Album.class, album.getId()); assertNull(fetchedAlbum2.getArtist()); } public void testFetchAssociatedEntityAlreadyInPersistenceContext() { // make them final Artist artist = new Artist(); artist.setId(99L); // Artist uses user-assigned/non-generated IDs artist.setName("The Beatles"); final Genre rock = new Genre("Rock"); artist.addGenre(rock); // store it final EntityManager em = getEntityManager(); em.persist(artist); // should cascade onto Rock genre, which is in the collection relation em.flush(); assertNotNull(rock.getId()); // ensure both are in the persistence context assertSame(artist, em.find(Artist.class, artist.getId())); assertSame(rock, em.find(Genre.class, rock.getId())); em.clear(); // prefetch the genre to get it into the persistence context final Genre fetchedRock = em.find(Genre.class, rock.getId()); assertNotNull(fetchedRock); final Artist fetchedArtist = em.find(Artist.class, artist.getId()); assertNotNull(fetchedArtist); assertEquals(1, fetchedArtist.getGenres().size()); final Genre cascadeFetchedGenre = fetchedArtist.getGenres().iterator().next(); assertSame(fetchedRock, cascadeFetchedGenre); } public void testPersistNewEntityLifecycle() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); final List<CallbackLogEntry> expectedLifecycle = new ArrayList<>(); assertEquals(expectedLifecycle, Album.CALLBACK_LOG); // store it final EntityManager em = getEntityManager(); em.persist(album); // the standalone listener is always notified before the entity itself (JPA2 section 3.5.4) expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PostPersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PostPersist.class)); /* * Do not assert above before flushing! * There is a difference of behaviour between Errai and Hibernate * where PostPersist happens after a flush in Hibernate, but before * a flush in Errai. */ em.flush(); assertCallbackLog(expectedLifecycle); // verify that detach causes no lifecycle updates em.detach(album); assertCallbackLog(expectedLifecycle); } public void testFetchEntityLifecycle() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); em.detach(album); final List<CallbackLogEntry> expectedLifecycle = new ArrayList<>(); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PostPersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PostPersist.class)); assertCallbackLog(expectedLifecycle); // fetch a fresh copy final Album fetchedAlbum = em.find(Album.class, album.getId()); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(fetchedAlbum), PostLoad.class)); expectedLifecycle.add(new CallbackLogEntry(fetchedAlbum, PostLoad.class)); assertCallbackLog(expectedLifecycle); // fetch again; expect no more PostLoad notifications final Album fetchedAlbum2 = em.find(Album.class, album.getId()); assertSame(fetchedAlbum, fetchedAlbum2); assertCallbackLog(expectedLifecycle); } private void assertCallbackLog(final List<CallbackLogEntry> expectedLifecycle) { assertEquals(expectedLifecycle.size(), Album.CALLBACK_LOG.size()); for (int i = 0; i < expectedLifecycle.size(); i++) { final CallbackLogEntry expected = expectedLifecycle.get(i); final CallbackLogEntry observed = Album.CALLBACK_LOG.get(i); try { assertEquals(expected, observed); } catch (final AssertionError ae) { throw new AssertionError("Index " + i + " differed from the expected log entry.", ae); } } /* * Clear the logs because subsequent test steps can modify * previous log entires causing false positives. */ Album.CALLBACK_LOG.clear(); expectedLifecycle.clear(); } public void testRemoveEntityLifecycle() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); em.detach(album); final List<CallbackLogEntry> expectedLifecycle = new ArrayList<>(); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PostPersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PostPersist.class)); assertCallbackLog(expectedLifecycle); // fetch a fresh copy final Album fetchedAlbum = em.find(Album.class, album.getId()); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(fetchedAlbum), PostLoad.class)); expectedLifecycle.add(new CallbackLogEntry(fetchedAlbum, PostLoad.class)); assertCallbackLog(expectedLifecycle); // delete it em.remove(fetchedAlbum); em.flush(); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(fetchedAlbum), PreRemove.class)); expectedLifecycle.add(new CallbackLogEntry(fetchedAlbum, PreRemove.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(fetchedAlbum), PostRemove .class)); expectedLifecycle.add(new CallbackLogEntry(fetchedAlbum, PostRemove.class)); assertCallbackLog(expectedLifecycle); } public void testUpdateEntityLifecycle() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); final List<CallbackLogEntry> expectedLifecycle = new ArrayList<>(); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PostPersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PostPersist.class)); assertCallbackLog(expectedLifecycle); // modify it album.setName("Cowabunga"); em.flush(); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PreUpdate.class)); expectedLifecycle.add(new CallbackLogEntry(album, PreUpdate.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PostUpdate.class)); expectedLifecycle.add(new CallbackLogEntry(album, PostUpdate.class)); assertCallbackLog(expectedLifecycle); } public void testMergeIntoManagedEntityLifecycle() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); final List<CallbackLogEntry> expectedLifecycle = new ArrayList<>(); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PostPersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PostPersist.class)); assertCallbackLog(expectedLifecycle); // create a detached version of the same album, and merge the change final Album mergeMe = new Album(); mergeMe.setId(album.getId()); // same ID mergeMe.setArtist(null); mergeMe.setName("Cowabunga"); // new name mergeMe.setReleaseDate(new Date(-8366400000L)); em.merge(mergeMe); em.flush(); // all the events should be delivered to album (the managed instance) -- NOT mergeMe, which remains detached expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PreUpdate.class)); expectedLifecycle.add(new CallbackLogEntry(album, PreUpdate.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PostUpdate.class)); expectedLifecycle.add(new CallbackLogEntry(album, PostUpdate.class)); assertCallbackLog(expectedLifecycle); } public void testMergeDetachedEntityLifecycle() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // store & detach it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); em.clear(); final List<CallbackLogEntry> expectedLifecycle = new ArrayList<>(); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(album), PostPersist.class)); expectedLifecycle.add(new CallbackLogEntry(album, PostPersist.class)); assertCallbackLog(expectedLifecycle); // create a detached version of the same album, and merge the change album.setName("Cowabunga"); em.merge(album); em.flush(); // events should be delivered to the merge target (the newly loaded managed instance) -- NOT album, which remains // detached final Album mergeTarget = em.find(Album.class, album.getId()); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(mergeTarget), PostLoad.class)); expectedLifecycle.add(new CallbackLogEntry(mergeTarget, PostLoad.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(mergeTarget), PreUpdate.class)); expectedLifecycle.add(new CallbackLogEntry(mergeTarget, PreUpdate.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(mergeTarget), PostUpdate.class)); expectedLifecycle.add(new CallbackLogEntry(mergeTarget, PostUpdate.class)); assertCallbackLog(expectedLifecycle); } public void testMergeNewEntityLifecycle() throws Exception { // make it final Album album = new Album(); album.setArtist(null); album.setName("Abbey Road"); album.setReleaseDate(new Date(-8366400000L)); // merge it right away (state=NEW -> state=MANAGED) final EntityManager em = getEntityManager(); final Album mergeTarget = em.merge(album); em.flush(); final List<CallbackLogEntry> expectedLifecycle = new ArrayList<>(); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(mergeTarget), PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(mergeTarget, PrePersist.class)); expectedLifecycle.add(new CallbackLogEntry(StandaloneLifecycleListener.instanceFor(mergeTarget), PostPersist.class)); expectedLifecycle.add(new CallbackLogEntry(mergeTarget, PostPersist.class)); assertCallbackLog(expectedLifecycle); } public void testStoreAndFetchOneWithEverythingUsingFieldAccess() throws Exception { final Timestamp timestamp = new Timestamp(1234L); timestamp.setNanos(4321); final Zentity original = new Zentity( true, Boolean.FALSE, (byte) -10, Byte.valueOf((byte) -10), new byte[] { -128, 0, 127, 126, 125, 124 }, new Byte[] { -128, 0, 127, -3 }, 'a', 'a', new char[] {'\u1234', '\u0000', 'a' }, new Character[] {'\u1234', '\u0000', 'a' }, Short.MIN_VALUE, Short.valueOf(Short.MIN_VALUE), Integer.MIN_VALUE, Integer.valueOf(Integer.MIN_VALUE), Long.MIN_VALUE, Long.valueOf(Long.MIN_VALUE), Float.MIN_VALUE, Float.valueOf(Float.MIN_VALUE), Double.MIN_VALUE, Double.valueOf(Double.MIN_VALUE), "A string with \u4292 non-ascii char", BigInteger.TEN, BigDecimal.TEN, new java.util.Date(1234L), new java.sql.Date(1234L), new Time(1234L), timestamp, PersistenceContextType.TRANSACTION); // store it final EntityManager em = getEntityManager(); em.persist(original); em.flush(); assertNotNull(original.getId()); em.clear(); final Zentity fetched = em.find(Zentity.class, original.getId()); assertNotSame(original, fetched); assertEquals(original.toString(), fetched.toString()); } public void testStoreAndFetchOneWithEverythingUsingMethodAccess() throws Exception { final Timestamp timestamp = new Timestamp(1234L); timestamp.setNanos(4321); final MethodAccessedZentity original = new MethodAccessedZentity( true, Boolean.FALSE, (byte) -10, Byte.valueOf((byte) -10), new byte[] { -128, 0, 127, 126, 125, 124 }, new Byte[] { -128, 0, 127, -3 }, 'a', 'a', new char[] {'\u1234', '\u0000', 'a' }, new Character[] {'\u1234', '\u0000', 'a' }, Short.MIN_VALUE, Short.valueOf(Short.MIN_VALUE), Integer.MIN_VALUE, Integer.valueOf(Integer.MIN_VALUE), Long.MIN_VALUE, Long.valueOf(Long.MIN_VALUE), Float.MIN_VALUE, Float.valueOf(Float.MIN_VALUE), Double.MIN_VALUE, Double.valueOf(Double.MIN_VALUE), "A string with \u4292 non-ascii char", BigInteger.TEN, BigDecimal.TEN, new java.util.Date(1234L), new java.sql.Date(1234L), new Time(1234L), timestamp, PersistenceContextType.TRANSACTION); // store it final EntityManager em = getEntityManager(); em.persist(original); em.flush(); assertNotNull(original.getId()); em.clear(); final MethodAccessedZentity fetched = em.find(MethodAccessedZentity.class, original.getId()); assertNotSame(original, fetched); assertEquals(original.toString(), fetched.toString()); } /** * This is to ensure that the null value of all nullable types can be marshalled and demarshalled without incident. */ public void testStoreAndFetchOneWithEverythingDefaultValues() throws Exception { final Zentity original = new Zentity(); // store it final EntityManager em = getEntityManager(); em.persist(original); em.flush(); assertNotNull(original.getId()); em.clear(); final Zentity fetched = em.find(Zentity.class, original.getId()); assertNotSame(original, fetched); assertEquals(original.toString(), fetched.toString()); } /** * This test ensures that application developers can add a primitive field to * a pre-existing entity class that may have persisted instances out in the * wild. Previously, trying to retrieve an old instance of an entity with a * new primitive attribute would cause a NullPointerException when the * generated code tried to assign <tt>null</tt> to the field. */ public void testAddPrimitiveFieldToPreviouslyPersistedEntity() { final Zentity original = new Zentity(); final EntityManager em = getEntityManager(); em.persist(original); em.flush(); assertNotNull(original.getId()); em.clear(); // now we pull the JSON out of local storage and yank out the primitiveInt. // the idea is to simulate having stored a version of Zentity that didn't have the primitiveInt attribute final Key<Zentity, Long> key = Key.get((ErraiEntityManager) em, Zentity.class, original.getId()); final String originalZentityJson = LocalStorage.get(key.toJson()); final JSONObject jsonEntity = JSONParser.parseStrict(originalZentityJson).isObject(); assertTrue("Sanity check failed: didn't find primitiveInt stored in backend entry: " + originalZentityJson, jsonEntity.containsKey("primitiveInt")); // this actually removes the key from the object jsonEntity.put("primitiveInt", null); assertFalse(jsonEntity.containsKey("primitiveInt")); LocalStorage.put(key.toJson(), jsonEntity.toString()); // now try and retrieve this "old version" of Zentity final Zentity fetched = em.find(Zentity.class, original.getId()); // <-- this line used to blow up with NPE assertNotSame(original, fetched); assertEquals(original.toString(), fetched.toString()); } /** * Ensures the ErraiEntityManager transparently recognizes wrapped/proxied * entities. */ public void testPersistProxiedEntity() { // make it Album album = new Album(); class AlbumProxy extends Album implements WrappedPortable { private final Album wrapped; AlbumProxy(final Album wrapme) { wrapped = wrapme; } @Override public Object unwrap() { return wrapped; } @Override public Long getId() { return wrapped.getId(); } @Override public void setId(final Long id) { wrapped.setId(id); } @Override public String getName() { return wrapped.getName(); } @Override public Artist getArtist() { return wrapped.getArtist(); } @Override public Date getReleaseDate() { return wrapped.getReleaseDate(); } @Override public void setName(final String name) { wrapped.setName(name); } @Override public void setArtist(final Artist artist) { wrapped.setArtist(artist); } @Override public void setReleaseDate(final Date releaseDate) { wrapped.setReleaseDate(releaseDate); } @Override public Format getFormat() { return wrapped.getFormat(); } @Override public void setFormat(final Format format) { wrapped.setFormat(format); } @Override public int hashCode() { return wrapped.hashCode(); } @Override public String toString() { return wrapped.toString(); } @Override public void postLoad() { wrapped.postLoad(); } @Override public boolean equals(final Object obj) { return wrapped.equals(obj); } } album = new AlbumProxy(album); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); em.detach(album); assertNotNull(album.getId()); // fetch it final Album fetchedAlbum = em.find(Album.class, album.getId()); assertNotSame(album, fetchedAlbum); assertEquals(album.toString(), fetchedAlbum.toString()); } /** * Ensures the ErraiEntityManager transparently recognizes wrapped/proxied * entities. */ public void testUpdateDataBinderProxiedEntity() { // make it Album album = new Album(); final TextBox box = new TextBox(); final DataBinder<Album> binder = DataBinder.forModel(album); album = binder.bind(box, "id").getModel(); assertEquals("", box.getText()); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); em.detach(album); assertNotNull(album.getId()); assertEquals(String.valueOf(album.getId()), box.getText()); } public void testEnsurePropertyChangeEventIsFiredAfterIdGeneration() { final DataBinder<Album> binder = DataBinder.forType(Album.class); final Album album = binder.getModel(); assertNull(album.getId()); final Album eventAlbum = new Album(); assertNull(eventAlbum.getId()); binder.addPropertyChangeHandler(new PropertyChangeHandler<Long>() { @Override public void onPropertyChange(final PropertyChangeEvent<Long> event) { if (event.getPropertyName().equals("id")) { eventAlbum.setId(event.getNewValue()); } else { fail("Unexpected property change event received for: " + event.getPropertyName()); } } }); // store it final EntityManager em = getEntityManager(); em.persist(album); em.flush(); em.detach(album); assertNotNull(album.getId()); assertEquals(album.getId(), eventAlbum.getId()); } public void testNullCollectionInEntity() throws Exception { final Artist artist = new Artist(); artist.setId(4433443L); artist.setGenres(null); // store it final EntityManager em = getEntityManager(); em.persist(artist); em.flush(); em.detach(artist); final Artist fetchedArtist = em.find(Artist.class, artist.getId()); assertNotNull(fetchedArtist.getGenres()); assertEquals(0, fetchedArtist.getGenres().size()); } public void testNullSingularReferenceInEntity() throws Exception { final CascadeFrom from = new CascadeFrom(); from.setAll(null); // store it final EntityManager em = getEntityManager(); em.persist(from); em.flush(); em.detach(from); final CascadeFrom fetchedFrom = em.find(CascadeFrom.class, Long.valueOf(from.getId())); assertNull(fetchedFrom.getAll()); } }