DATAJPA-581, DATAJPA-582 - Fixed isNew(…) detection in JpaMetamodelEntityInformation.

We now generally consider all non-null values of a non-primitive @Version attribute indicating a non-new entity.
This commit is contained in:
Oliver Gierke
2014-08-01 19:49:38 +02:00
parent 6341e41065
commit dfcaeb6a52
10 changed files with 245 additions and 107 deletions

View File

@@ -200,11 +200,7 @@ public class JpaMetamodelEntityInformation<T, ID extends Serializable> extends J
BeanWrapper wrapper = new DirectFieldAccessFallbackBeanWrapper(entity);
Object versionValue = wrapper.getPropertyValue(versionAttribute.getName());
if (versionValue == null) {
return true;
}
return ((Number) versionValue).longValue() == 0;
return versionValue == null;
}
/**

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2012 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
*
* 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.springframework.data.jpa.domain.sample;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import org.springframework.data.domain.Persistable;
/**
* Sample entity using {@link IdClass} annotation to demarcate ids.
*
* @author Oliver Gierke
*/
@Entity
@IdClass(PersistableWithIdClassPK.class)
public class PersistableWithIdClass implements Persistable<PersistableWithIdClassPK> {
private static final long serialVersionUID = 1L;
@Id
Long first;
@Id
Long second;
private boolean isNew;
protected PersistableWithIdClass() {
this.isNew = true;
}
public PersistableWithIdClass(Long first, Long second) {
this.first = first;
this.second = second;
this.isNew = true;
}
/**
* @return the first
*/
public Long getFirst() {
return first;
}
/**
* @return the second
*/
public Long getSecond() {
return second;
}
/* (non-Javadoc)
* @see org.springframework.data.domain.Persistable#getId()
*/
public PersistableWithIdClassPK getId() {
return new PersistableWithIdClassPK(first, second);
}
/* (non-Javadoc)
* @see org.springframework.data.domain.Persistable#isNew()
*/
public boolean isNew() {
return this.isNew;
}
public void setNotNew() {
this.isNew = false;
}
}

View File

@@ -23,18 +23,18 @@ import java.io.Serializable;
*
* @author Oliver Gierke
*/
public class SampleWithIdClassPK implements Serializable {
public class PersistableWithIdClassPK implements Serializable {
private static final long serialVersionUID = 23126782341L;
private Long first;
private Long second;
public SampleWithIdClassPK() {
public PersistableWithIdClassPK() {
}
public SampleWithIdClassPK(Long first, Long second) {
public PersistableWithIdClassPK(Long first, Long second) {
this.first = first;
this.second = second;
}
@@ -62,7 +62,7 @@ public class SampleWithIdClassPK implements Serializable {
return false;
}
SampleWithIdClassPK that = (SampleWithIdClassPK) obj;
PersistableWithIdClassPK that = (PersistableWithIdClassPK) obj;
return nullSafeEquals(this.first, that.first) && nullSafeEquals(this.second, that.second);
}

View File

@@ -1,84 +1,54 @@
/*
* Copyright 2012 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
*
* 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.springframework.data.jpa.domain.sample;
import java.io.Serializable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import org.springframework.data.domain.Persistable;
/**
* Sample entity using {@link IdClass} annotation to demarcate ids.
*
* @author Oliver Gierke
*/
@Entity
@IdClass(SampleWithIdClassPK.class)
public class SampleWithIdClass implements Persistable<SampleWithIdClassPK> {
@IdClass(SampleWithIdClass.SampleWithIdClassPK.class)
@Access(AccessType.FIELD)
public class SampleWithIdClass {
private static final long serialVersionUID = 1L;
@Id Long first;
@Id Long second;
@Id
Long first;
@SuppressWarnings("serial")
public static class SampleWithIdClassPK implements Serializable {
@Id
Long second;
Long first;
Long second;
private boolean isNew;
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
protected SampleWithIdClass() {
this.isNew = true;
}
if (obj == this) {
return true;
}
public SampleWithIdClass(Long first, Long second) {
this.first = first;
this.second = second;
this.isNew = true;
}
if (!(obj instanceof SampleWithIdClassPK)) {
return false;
}
/**
* @return the first
*/
public Long getFirst() {
return first;
}
SampleWithIdClassPK that = (SampleWithIdClassPK) obj;
/**
* @return the second
*/
public Long getSecond() {
return second;
}
return this.first.equals(that.first) && this.second.equals(that.second);
}
/* (non-Javadoc)
* @see org.springframework.data.domain.Persistable#getId()
*/
public SampleWithIdClassPK getId() {
return new SampleWithIdClassPK(first, second);
}
/* (non-Javadoc)
* @see org.springframework.data.domain.Persistable#isNew()
*/
public boolean isNew() {
return this.isNew;
}
public void setNotNew() {
this.isNew = false;
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return first.hashCode() + second.hashCode();
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2014 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
*
* 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.springframework.data.jpa.domain.sample;
import java.sql.Timestamp;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class SampleWithTimestampVersion {
public @Id Long id;
public @Version Timestamp version;
}

View File

@@ -56,6 +56,14 @@ public class EclipseLinkJpaMetamodelEntityInformationIntegrationTests extends
@Ignore
public void detectsNewStateForEntityWithPrimitiveId() {}
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests#considersEntityWithUnSetCompundIdNew()
*/
@Override
@Ignore
public void considersEntityWithUnsetCompundIdNew() {}
@Override
protected String getMetadadataPersitenceUnitName() {
return "metadata_el";

View File

@@ -19,6 +19,8 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import javax.persistence.Access;
import javax.persistence.AccessType;
@@ -36,11 +38,14 @@ import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.AbstractPersistable;
import org.springframework.data.jpa.domain.sample.PersistableWithIdClass;
import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK;
import org.springframework.data.jpa.domain.sample.PrimitiveVersionProperty;
import org.springframework.data.jpa.domain.sample.Role;
import org.springframework.data.jpa.domain.sample.SampleWithIdClass;
import org.springframework.data.jpa.domain.sample.SampleWithIdClassPK;
import org.springframework.data.jpa.domain.sample.SampleWithIdClass.SampleWithIdClassPK;
import org.springframework.data.jpa.domain.sample.SampleWithPrimitiveId;
import org.springframework.data.jpa.domain.sample.SampleWithTimestampVersion;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.domain.sample.VersionedUser;
import org.springframework.data.repository.core.EntityInformation;
@@ -88,9 +93,9 @@ public class JpaMetamodelEntityInformationIntegrationTests {
@Test
public void detectsIdClass() {
EntityInformation<SampleWithIdClass, ?> information = JpaEntityInformationSupport.getMetadata(
SampleWithIdClass.class, em);
assertThat(information.getIdType(), is(typeCompatibleWith(SampleWithIdClassPK.class)));
EntityInformation<PersistableWithIdClass, ?> information = JpaEntityInformationSupport.getMetadata(
PersistableWithIdClass.class, em);
assertThat(information.getIdType(), is(typeCompatibleWith(PersistableWithIdClassPK.class)));
}
/**
@@ -99,14 +104,14 @@ public class JpaMetamodelEntityInformationIntegrationTests {
@Test
public void returnsIdInstanceCorrectly() {
SampleWithIdClass entity = new SampleWithIdClass(2L, 4L);
PersistableWithIdClass entity = new PersistableWithIdClass(2L, 4L);
JpaEntityInformation<SampleWithIdClass, ?> information = JpaEntityInformationSupport.getMetadata(
SampleWithIdClass.class, em);
JpaEntityInformation<PersistableWithIdClass, ?> information = JpaEntityInformationSupport.getMetadata(
PersistableWithIdClass.class, em);
Object id = information.getId(entity);
assertThat(id, is(instanceOf(SampleWithIdClassPK.class)));
assertThat(id, is((Object) new SampleWithIdClassPK(2L, 4L)));
assertThat(id, is(instanceOf(PersistableWithIdClassPK.class)));
assertThat(id, is((Object) new PersistableWithIdClassPK(2L, 4L)));
}
/**
@@ -216,6 +221,51 @@ public class JpaMetamodelEntityInformationIntegrationTests {
assertThat(information.isNew(pvp), is(false));
}
/**
* @see DATAJPA-582
*/
@Test
public void considersEntityWithUnsetCompundIdNew() {
EntityInformation<SampleWithIdClass, SampleWithIdClassPK> information = new JpaMetamodelEntityInformation<SampleWithIdClass, SampleWithIdClassPK>(
SampleWithIdClass.class, em.getMetamodel());
assertThat(information.isNew(new SampleWithIdClass()), is(true));
}
/**
* @see DATAJPA-582
*/
@Test
public void considersEntityWithSetTimestampVersionNotNew() {
EntityInformation<SampleWithTimestampVersion, Long> information = getEntityInformation(SampleWithTimestampVersion.class);
SampleWithTimestampVersion entity = new SampleWithTimestampVersion();
entity.version = new Timestamp(new Date().getTime());
assertThat(information.isNew(entity), is(false));
}
/**
* @see DATAJPA-582, DATAJPA-581
*/
@Test
public void considersEntityWithNonPrimitiveNonNullIdTypeNotNew() {
EntityInformation<User, Long> information = getEntityInformation(User.class);
User user = new User();
assertThat(information.isNew(user), is(true));
user.setId(0);
assertThat(information.isNew(user), is(false));
}
private <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainType) {
return new JpaMetamodelEntityInformation<T, ID>(domainType, em.getMetamodel());
}
protected String getMetadadataPersitenceUnitName() {
return "metadata";
}
@@ -241,5 +291,4 @@ public class JpaMetamodelEntityInformationIntegrationTests {
public static class Sample extends Identifiable {
}
}

View File

@@ -34,8 +34,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.jpa.domain.sample.SampleWithIdClass;
import org.springframework.data.jpa.domain.sample.SampleWithIdClassPK;
import org.springframework.data.jpa.domain.sample.PersistableWithIdClass;
import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK;
/**
* Unit tests for {@link JpaMetamodelEntityInformation}.
@@ -49,9 +49,9 @@ public class JpaMetamodelEntityInformationUnitTests {
Metamodel metamodel;
@Mock
IdentifiableType<SampleWithIdClass> type;
IdentifiableType<PersistableWithIdClass> type;
@Mock
SingularAttribute<SampleWithIdClass, ?> first, second;
SingularAttribute<PersistableWithIdClass, ?> first, second;
@Mock
@SuppressWarnings("rawtypes")
@@ -63,15 +63,15 @@ public class JpaMetamodelEntityInformationUnitTests {
when(first.getName()).thenReturn("first");
when(second.getName()).thenReturn("second");
Set<SingularAttribute<? super SampleWithIdClass, ?>> attributes = new HashSet<SingularAttribute<? super SampleWithIdClass, ?>>(
Set<SingularAttribute<? super PersistableWithIdClass, ?>> attributes = new HashSet<SingularAttribute<? super PersistableWithIdClass, ?>>(
asList(first, second));
when(type.getIdClassAttributes()).thenReturn(attributes);
when(metamodel.managedType(SampleWithIdClass.class)).thenReturn(type);
when(metamodel.managedType(PersistableWithIdClass.class)).thenReturn(type);
when(type.getIdType()).thenReturn(idType);
when(idType.getJavaType()).thenReturn(SampleWithIdClassPK.class);
when(idType.getJavaType()).thenReturn(PersistableWithIdClassPK.class);
}
/**
@@ -80,13 +80,13 @@ public class JpaMetamodelEntityInformationUnitTests {
@Test
public void doesNotCreateIdIfAllPartialAttributesAreNull() {
JpaMetamodelEntityInformation<SampleWithIdClass, Serializable> information = new JpaMetamodelEntityInformation<SampleWithIdClass, Serializable>(
SampleWithIdClass.class, metamodel);
JpaMetamodelEntityInformation<PersistableWithIdClass, Serializable> information = new JpaMetamodelEntityInformation<PersistableWithIdClass, Serializable>(
PersistableWithIdClass.class, metamodel);
SampleWithIdClass entity = new SampleWithIdClass(null, null);
PersistableWithIdClass entity = new PersistableWithIdClass(null, null);
assertThat(information.getId(entity), is(nullValue()));
entity = new SampleWithIdClass(2L, null);
entity = new PersistableWithIdClass(2L, null);
assertThat(information.getId(entity), is(notNullValue()));
}
}

View File

@@ -28,8 +28,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.sample.SampleEntity;
import org.springframework.data.jpa.domain.sample.SampleEntityPK;
import org.springframework.data.jpa.domain.sample.SampleWithIdClass;
import org.springframework.data.jpa.domain.sample.SampleWithIdClassPK;
import org.springframework.data.jpa.domain.sample.PersistableWithIdClass;
import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.test.context.ContextConfiguration;
@@ -50,7 +50,7 @@ public class JpaRepositoryTests {
@PersistenceContext EntityManager em;
JpaRepository<SampleEntity, SampleEntityPK> repository;
CrudRepository<SampleWithIdClass, SampleWithIdClassPK> idClassRepository;
CrudRepository<PersistableWithIdClass, PersistableWithIdClassPK> idClassRepository;
@Before
public void setUp() {
@@ -79,13 +79,13 @@ public class JpaRepositoryTests {
@Test
public void executesCrudOperationsForEntityWithIdClass() {
SampleWithIdClass entity = new SampleWithIdClass(1L, 1L);
PersistableWithIdClass entity = new PersistableWithIdClass(1L, 1L);
idClassRepository.save(entity);
assertThat(entity.getFirst(), is(notNullValue()));
assertThat(entity.getSecond(), is(notNullValue()));
SampleWithIdClassPK id = new SampleWithIdClassPK(entity.getFirst(), entity.getSecond());
PersistableWithIdClassPK id = new PersistableWithIdClassPK(entity.getFirst(), entity.getSecond());
assertThat(idClassRepository.findOne(id), is(entity));
}
@@ -96,12 +96,12 @@ public class JpaRepositoryTests {
@Test
public void testExistsForDomainObjectsWithCompositeKeys() throws Exception {
SampleWithIdClass s1 = idClassRepository.save(new SampleWithIdClass(1L, 1L));
SampleWithIdClass s2 = idClassRepository.save(new SampleWithIdClass(2L, 2L));
PersistableWithIdClass s1 = idClassRepository.save(new PersistableWithIdClass(1L, 1L));
PersistableWithIdClass s2 = idClassRepository.save(new PersistableWithIdClass(2L, 2L));
assertThat(idClassRepository.exists(s1.getId()), is(true));
assertThat(idClassRepository.exists(s2.getId()), is(true));
assertThat(idClassRepository.exists(new SampleWithIdClassPK(1L, 2L)), is(false));
assertThat(idClassRepository.exists(new PersistableWithIdClassPK(1L, 2L)), is(false));
}
/**
@@ -110,13 +110,13 @@ public class JpaRepositoryTests {
@Test
public void executesExistsForEntityWithIdClass() {
SampleWithIdClass entity = new SampleWithIdClass(1L, 1L);
PersistableWithIdClass entity = new PersistableWithIdClass(1L, 1L);
idClassRepository.save(entity);
assertThat(entity.getFirst(), is(notNullValue()));
assertThat(entity.getSecond(), is(notNullValue()));
SampleWithIdClassPK id = new SampleWithIdClassPK(entity.getFirst(), entity.getSecond());
PersistableWithIdClassPK id = new PersistableWithIdClassPK(entity.getFirst(), entity.getSecond());
assertThat(idClassRepository.exists(id), is(true));
}
@@ -125,7 +125,7 @@ public class JpaRepositoryTests {
}
private static interface SampleWithIdClassRepository extends CrudRepository<SampleWithIdClass, SampleWithIdClassPK> {
private static interface SampleWithIdClassRepository extends CrudRepository<PersistableWithIdClass, PersistableWithIdClassPK> {
}
}

View File

@@ -11,6 +11,7 @@
<class>org.springframework.data.jpa.domain.sample.AuditableRole</class>
<class>org.springframework.data.jpa.domain.sample.Parent</class>
<class>org.springframework.data.jpa.domain.sample.PrimitiveVersionProperty</class>
<class>org.springframework.data.jpa.domain.sample.PersistableWithIdClass</class>
<class>org.springframework.data.jpa.domain.sample.Child</class>
<class>org.springframework.data.jpa.domain.sample.Role</class>
<class>org.springframework.data.jpa.domain.sample.SpecialUser</class>
@@ -19,6 +20,7 @@
<class>org.springframework.data.jpa.domain.sample.SampleEntityPK</class>
<class>org.springframework.data.jpa.domain.sample.SampleWithIdClass</class>
<class>org.springframework.data.jpa.domain.sample.SampleWithPrimitiveId</class>
<class>org.springframework.data.jpa.domain.sample.SampleWithTimestampVersion</class>
<class>org.springframework.data.jpa.domain.sample.VersionedUser</class>
<class>org.springframework.data.jpa.domain.sample.AbstractMappedType</class>
<class>org.springframework.data.jpa.domain.sample.ConcreteType1</class>