Could not save Composition when master-entity is inheritted with the "joined" strategy #1013

This commit is contained in:
Andrey Subbotin 2018-07-31 12:37:27 +04:00
parent 7ebddfb683
commit dfec903dc1
8 changed files with 197 additions and 7 deletions

View File

@ -452,6 +452,13 @@ configure(coreModule) {
]
}
task testJoinedComposition(type: Test) {
scanForTestClasses = false
includes = ['spec/cuba/core/composition/joined_composition/JoinedCompositionTestClass.class']
}
test.finalizedBy testJoinedComposition
assembleDbScripts {
moduleAlias = '10-cuba'
}

View File

@ -20,6 +20,7 @@ package com.haulmont.cuba.core.sys.persistence;
import com.google.common.base.Strings;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.chile.core.model.Range;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.SoftDelete;
import com.haulmont.cuba.core.entity.annotation.EmbeddedParameters;
@ -31,21 +32,16 @@ import com.haulmont.cuba.core.sys.UuidConverter;
import org.apache.commons.lang3.BooleanUtils;
import org.eclipse.persistence.annotations.CacheCoordinationType;
import org.eclipse.persistence.config.CacheIsolationType;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorEventListener;
import org.eclipse.persistence.descriptors.DescriptorEventManager;
import org.eclipse.persistence.descriptors.*;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.*;
import org.eclipse.persistence.platform.database.PostgreSQLPlatform;
import org.eclipse.persistence.queries.ReadQuery;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.SessionEvent;
import org.eclipse.persistence.sessions.SessionEventAdapter;
import javax.persistence.OneToOne;
import java.lang.reflect.AnnotatedElement;
import java.sql.Types;
import java.util.List;
import java.util.Map;
@ -64,13 +60,17 @@ public class EclipseLinkSessionEventListener extends SessionEventAdapter {
setPrintInnerJoinOnClause(session);
Map<Class, ClassDescriptor> descriptorMap = session.getDescriptors();
boolean hasMultipleTableConstraintDependency = hasMultipleTableConstraintDependency();
for (Map.Entry<Class, ClassDescriptor> entry : descriptorMap.entrySet()) {
MetaClass metaClass = metadata.getSession().getClassNN(entry.getKey());
ClassDescriptor desc = entry.getValue();
setCacheable(metaClass, desc, session);
if (hasMultipleTableConstraintDependency) {
setMultipleTableConstraintDependency(metaClass, desc);
}
if (Entity.class.isAssignableFrom(desc.getJavaClass())) {
// set DescriptorEventManager that doesn't invoke listeners for base classes
desc.setEventManager(new DescriptorEventManager() {
@ -165,6 +165,24 @@ public class EclipseLinkSessionEventListener extends SessionEventAdapter {
}
}
private void setMultipleTableConstraintDependency(MetaClass metaClass, ClassDescriptor desc) {
InheritancePolicy policy = desc.getInheritancePolicyOrNull();
if (policy != null && policy.isJoinedStrategy() && policy.getParentClass() != null) {
boolean hasOneToMany = metaClass.getOwnProperties().stream().anyMatch(metaProperty ->
metadata.getTools().isPersistent(metaProperty)
&& metaProperty.getRange().isClass()
&& metaProperty.getRange().getCardinality() == Range.Cardinality.ONE_TO_MANY);
if (hasOneToMany) {
desc.setHasMultipleTableConstraintDependecy(true);
}
}
}
private boolean hasMultipleTableConstraintDependency() {
return BooleanUtils.toBoolean(
AppContext.getProperty("cuba.hasMultipleTableConstraintDependency"));
}
private void setDatabaseFieldParameters(Session session, DatabaseField field) {
if (session.getPlatform() instanceof PostgreSQLPlatform) {
field.setSqlType(Types.OTHER);

View File

@ -128,6 +128,21 @@ create table TEST_ROOT_ENTITY_DETAIL (
primary key (ID)
)^
create table TEST_CHILD_ENTITY_DETAIL (
ID varchar(36) not null,
CREATE_TS timestamp,
CREATED_BY varchar(50),
VERSION integer,
UPDATE_TS timestamp,
UPDATED_BY varchar(50),
DELETE_TS timestamp,
DELETED_BY varchar(50),
INFO varchar(255),
CHILD_ENTITY_ID varchar(36) not null,
constraint TEST_CHILD_ENTITY_DETAIL_CHILD_ENTITY foreign key (CHILD_ENTITY_ID) references TEST_CHILD_ENTITY(ENTITY_ID),
primary key (ID)
)^
------------------------------------------------------------------------------------------------------------
create table TEST_SOFT_DELETE_OTO_B (

View File

@ -16,7 +16,10 @@
package com.haulmont.cuba.testmodel.selfinherited;
import com.haulmont.chile.core.annotations.Composition;
import javax.persistence.*;
import java.util.List;
@DiscriminatorValue("C")
@Entity(name = "test$ChildEntity")
@ -28,6 +31,10 @@ public class ChildEntity extends RootEntity {
@Column(name = "NAME")
protected String name;
@OneToMany(mappedBy = "childEntity")
@Composition
protected List<ChildEntityDetail> childDetails;
public String getName() {
return name;
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2008-2018 Haulmont.
*
* 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 com.haulmont.cuba.testmodel.selfinherited;
import com.haulmont.cuba.core.entity.StandardEntity;
import javax.persistence.*;
@Table(name = "TEST_CHILD_ENTITY_DETAIL")
@Entity(name = "test$ChildEntityDetail")
public class ChildEntityDetail extends StandardEntity {
private static final long serialVersionUID = -3794606860555343620L;
@Column(name = "INFO")
protected String info;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CHILD_ENTITY_ID")
protected ChildEntity childEntity;
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public ChildEntity getChildEntity() {
return childEntity;
}
public void setChildEntity(ChildEntity childEntity) {
this.childEntity = childEntity;
}
}

View File

@ -34,6 +34,7 @@
<class>com.haulmont.cuba.testmodel.selfinherited.RootEntity</class>
<class>com.haulmont.cuba.testmodel.selfinherited.ChildEntity</class>
<class>com.haulmont.cuba.testmodel.selfinherited.RootEntityDetail</class>
<class>com.haulmont.cuba.testmodel.selfinherited.ChildEntityDetail</class>
<class>com.haulmont.cuba.testmodel.softdelete_one_to_one.SoftDeleteOneToOneA</class>
<class>com.haulmont.cuba.testmodel.softdelete_one_to_one.SoftDeleteOneToOneB</class>
<class>com.haulmont.cuba.testmodel.cascadedelete.CascadeEntity</class>

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2008-2018 Haulmont.
*
* 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 spec.cuba.core.composition.joined_composition
import com.haulmont.bali.db.QueryRunner
import com.haulmont.cuba.core.Persistence
import com.haulmont.cuba.core.global.AppBeans
import com.haulmont.cuba.core.global.DataManager
import com.haulmont.cuba.core.global.Metadata
import com.haulmont.cuba.testmodel.selfinherited.ChildEntity
import com.haulmont.cuba.testmodel.selfinherited.ChildEntityDetail
import com.haulmont.cuba.testsupport.TestContainer
import org.junit.ClassRule
import spock.lang.Shared
import spock.lang.Specification
class JoinedCompositionTestClass extends Specification {
@Shared
@ClassRule
public TestContainer cont = new TestContainer()
.setAppPropertiesFiles(Arrays.asList("cuba-app.properties", "test-app.properties", "cuba-test-app.properties", "/spec/cuba/core/composition/test-composition-app.properties"))
private Persistence persistence = cont.persistence()
private Metadata metadata = cont.metadata()
private dataManager
void setup() {
dataManager = AppBeans.get(DataManager)
}
void cleanup() {
def runner = new QueryRunner(persistence.dataSource)
runner.update('delete from TEST_CHILD_ENTITY_DETAIL')
runner.update('delete from TEST_ROOT_ENTITY_DETAIL')
runner.update('delete from TEST_CHILD_ENTITY')
runner.update('delete from TEST_ROOT_ENTITY')
}
def "store master-detail"() {
when:
persistence.runInTransaction({ em ->
ChildEntity childEntity = metadata.create(ChildEntity)
childEntity.name = 'name'
childEntity.description = 'description'
em.persist(childEntity)
ChildEntityDetail childEntityDetail = metadata.create(ChildEntityDetail)
childEntityDetail.childEntity = childEntity
childEntityDetail.info = 'info'
em.persist(childEntityDetail)
})
then:
noExceptionThrown()
}
}

View File

@ -0,0 +1,20 @@
#
# Copyright (c) 2008-2018 Haulmont.
#
# 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.
#
# This file contains properties needed only for testing inside CUBA.
# Do not include it to test properties of your project.
cuba.hasMultipleTableConstraintDependency = true