Test JPA cascade

This commit is contained in:
Konstantin Krivopustov 2018-04-16 12:31:48 +04:00
parent 157d057dd8
commit 4411dbc64f
9 changed files with 501 additions and 0 deletions

View File

@ -840,3 +840,53 @@ create table TEST_NUMBER_ID_SINGLE_TABLE_ROOT (
-- --
primary key (ID) primary key (ID)
)^ )^
------------------------------------------------------------------------------------------------------------------------
create table TEST_JPA_CASCADE_FOO (
ID varchar(36) not null,
VERSION integer not null,
CREATE_TS timestamp,
CREATED_BY varchar(50),
UPDATE_TS timestamp,
UPDATED_BY varchar(50),
DELETE_TS timestamp,
DELETED_BY varchar(50),
--
NAME varchar(255),
BAR_ID varchar(36),
--
primary key (ID)
)^
create table TEST_JPA_CASCADE_BAR (
ID varchar(36) not null,
VERSION integer not null,
CREATE_TS timestamp,
CREATED_BY varchar(50),
UPDATE_TS timestamp,
UPDATED_BY varchar(50),
DELETE_TS timestamp,
DELETED_BY varchar(50),
--
NAME varchar(255),
--
primary key (ID)
)^
create table TEST_JPA_CASCADE_ITEM (
ID varchar(36) not null,
VERSION integer not null,
CREATE_TS timestamp,
CREATED_BY varchar(50),
UPDATE_TS timestamp,
UPDATED_BY varchar(50),
DELETE_TS timestamp,
DELETED_BY varchar(50),
--
NAME varchar(255),
FOO_ID varchar(36),
--
primary key (ID)
)^

View File

@ -0,0 +1,39 @@
/*
* 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.jpa_cascade;
import com.haulmont.cuba.core.entity.StandardEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity(name = "test$JpaCascadeBar")
@Table(name = "TEST_JPA_CASCADE_BAR")
public class JpaCascadeBar extends StandardEntity {
@Column(name = "NAME")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.jpa_cascade;
import com.haulmont.cuba.core.entity.StandardEntity;
import javax.persistence.*;
import java.util.List;
@Entity(name = "test$JpaCascadeFoo")
@Table(name = "TEST_JPA_CASCADE_FOO")
public class JpaCascadeFoo extends StandardEntity {
@Column(name = "NAME")
private String name;
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "BAR_ID")
private JpaCascadeBar bar;
@OneToMany(mappedBy = "foo", cascade = CascadeType.ALL, orphanRemoval = true)
private List<JpaCascadeItem> items;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public JpaCascadeBar getBar() {
return bar;
}
public void setBar(JpaCascadeBar bar) {
this.bar = bar;
}
public List<JpaCascadeItem> getItems() {
return items;
}
public void setItems(List<JpaCascadeItem> items) {
this.items = items;
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.jpa_cascade;
import com.haulmont.cuba.core.entity.StandardEntity;
import javax.persistence.*;
@Entity(name = "test$JpaCascadeItem")
@Table(name = "TEST_JPA_CASCADE_ITEM")
public class JpaCascadeItem extends StandardEntity {
@Column(name = "NAME")
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "FOO_ID")
private JpaCascadeFoo foo;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public JpaCascadeFoo getFoo() {
return foo;
}
public void setFoo(JpaCascadeFoo foo) {
this.foo = foo;
}
}

View File

@ -84,5 +84,9 @@
<class>com.haulmont.cuba.testmodel.number_id.NumberIdSingleTableRoot</class> <class>com.haulmont.cuba.testmodel.number_id.NumberIdSingleTableRoot</class>
<class>com.haulmont.cuba.testmodel.number_id.NumberIdSingleTableChild</class> <class>com.haulmont.cuba.testmodel.number_id.NumberIdSingleTableChild</class>
<class>com.haulmont.cuba.testmodel.number_id.NumberIdSingleTableGrandChild</class> <class>com.haulmont.cuba.testmodel.number_id.NumberIdSingleTableGrandChild</class>
<class>com.haulmont.cuba.testmodel.jpa_cascade.JpaCascadeFoo</class>
<class>com.haulmont.cuba.testmodel.jpa_cascade.JpaCascadeBar</class>
<class>com.haulmont.cuba.testmodel.jpa_cascade.JpaCascadeItem</class>
</persistence-unit> </persistence-unit>
</persistence> </persistence>

View File

@ -0,0 +1,175 @@
/*
* 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.jpa_cascade
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.EntityStates
import com.haulmont.cuba.core.global.Metadata
import com.haulmont.cuba.core.sys.listener.EntityListenerManager
import com.haulmont.cuba.testmodel.jpa_cascade.JpaCascadeBar
import com.haulmont.cuba.testmodel.jpa_cascade.JpaCascadeFoo
import com.haulmont.cuba.testmodel.jpa_cascade.JpaCascadeItem
import com.haulmont.cuba.testsupport.TestContainer
import org.junit.ClassRule
import spock.lang.Shared
import spock.lang.Specification
class JpaCascadeTest extends Specification {
@Shared @ClassRule
public TestContainer cont = TestContainer.Common.INSTANCE
private Metadata metadata
private Persistence persistence
private EntityStates entityStates
void setup() {
AppBeans.get(EntityListenerManager).addListener(JpaCascadeBar, TestJpaCascadeBarListener)
AppBeans.get(EntityListenerManager).addListener(JpaCascadeFoo, TestJpaCascadeFooListener)
AppBeans.get(EntityListenerManager).addListener(JpaCascadeItem, TestJpaCascadeItemListener)
TestJpaCascadeFooListener.messages.clear()
TestJpaCascadeBarListener.messages.clear()
TestJpaCascadeItemListener.messages.clear()
metadata = cont.metadata()
persistence = cont.persistence()
entityStates = AppBeans.get(EntityStates)
}
void cleanup() {
AppBeans.get(EntityListenerManager).removeListener(JpaCascadeFoo, TestJpaCascadeFooListener)
AppBeans.get(EntityListenerManager).removeListener(JpaCascadeBar, TestJpaCascadeBarListener)
AppBeans.get(EntityListenerManager).removeListener(JpaCascadeItem, TestJpaCascadeItemListener)
def runner = new QueryRunner(persistence.getDataSource())
runner.update('delete from TEST_JPA_CASCADE_FOO')
runner.update('delete from TEST_JPA_CASCADE_BAR')
runner.update('delete from TEST_JPA_CASCADE_ITEM')
}
def "many-to-one reference with cascade"() {
def isManaged, isNew
when: "foo.bar is persisted implicitly"
def foo = metadata.create(JpaCascadeFoo)
def bar = metadata.create(JpaCascadeBar)
foo.setBar(bar)
persistence.runInTransaction { em ->
em.persist(foo)
isManaged = entityStates.isManaged(foo.bar)
isNew = entityStates.isNew(foo.bar)
}
then: "EntityStates contracts are broken and entity listeners are not invoked"
!isManaged // EntityStates.isManaged contract is broken
isNew
TestJpaCascadeFooListener.messages.size() == 1
TestJpaCascadeFooListener.messages[0].startsWith('onBeforeInsert')
TestJpaCascadeBarListener.messages.size() == 0 // listeners on implicitly persisted entities are not invoked
// createTs/createdBy are correct
def foo1
persistence.runInTransaction { em ->
foo1 = em.find(JpaCascadeFoo, foo.id)
assert foo1.bar == bar
assert foo1.bar.createTs != null
assert foo1.bar.createdBy != null
}
when: "foo.bar is merged implicitly"
foo1.name = 'foo_changed'
foo1.bar.name = 'bar_changed'
persistence.runInTransaction { em ->
em.merge(foo1)
}
then: "value is saved and updateTs/updatedBy are correct"
def foo2
persistence.runInTransaction { em ->
foo2 = em.find(JpaCascadeFoo, foo.id)
assert foo2.bar.name == 'bar_changed'
assert foo2.bar.updateTs != null
assert foo2.bar.updatedBy != null
}
}
def "one-to-many collection with cascade and orphanRemoval"() {
def isManaged, isNew
when: "foo.items collection is persisted implicitly"
def foo = metadata.create(JpaCascadeFoo)
def item1 = metadata.create(JpaCascadeItem)
def item2 = metadata.create(JpaCascadeItem)
item1.foo = foo
item2.foo = foo
foo.setItems([item1, item2])
persistence.runInTransaction { em ->
em.persist(foo)
isManaged = entityStates.isManaged(foo.items[0])
isNew = entityStates.isNew(foo.items[0])
}
then: "EntityStates contracts are broken and entity listeners are not invoked"
!isManaged // EntityStates.isManaged contract is broken
isNew
TestJpaCascadeFooListener.messages.size() == 1
TestJpaCascadeFooListener.messages[0].startsWith('onBeforeInsert')
TestJpaCascadeBarListener.messages.size() == 0 // listeners on implicitly persisted entities are not invoked
// createTs/createdBy are correct
persistence.runInTransaction { em ->
def foo1 = em.find(JpaCascadeFoo, foo.id)
assert foo1.items[0].createTs != null
assert foo1.items[0].createdBy != null
}
when: "removing item from collection"
def removedItem
persistence.runInTransaction { em ->
def foo1 = em.find(JpaCascadeFoo, foo.id)
removedItem = foo1.items[0]
foo1.items.remove(removedItem)
}
then: "it is hard-deleted from the database although it is a soft-deleted entity"
persistence.runInTransaction { em ->
def foo1 = em.find(JpaCascadeFoo, foo.id)
assert foo1.items.size() == 1
em.softDeletion = false
assert em.find(JpaCascadeItem, removedItem.id) == null
}
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.jpa_cascade;
import com.haulmont.cuba.core.EntityManager;
import com.haulmont.cuba.core.listener.BeforeInsertEntityListener;
import com.haulmont.cuba.core.listener.BeforeUpdateEntityListener;
import com.haulmont.cuba.testmodel.jpa_cascade.JpaCascadeBar;
import java.util.ArrayList;
import java.util.List;
public class TestJpaCascadeBarListener implements
BeforeInsertEntityListener<JpaCascadeBar>, BeforeUpdateEntityListener<JpaCascadeBar> {
static List<String> messages = new ArrayList<>();
@Override
public void onBeforeInsert(JpaCascadeBar entity, EntityManager entityManager) {
messages.add("onBeforeInsert " + entity);
}
@Override
public void onBeforeUpdate(JpaCascadeBar entity, EntityManager entityManager) {
messages.add("onBeforeUpdate " + entity);
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.jpa_cascade;
import com.haulmont.cuba.core.EntityManager;
import com.haulmont.cuba.core.listener.BeforeInsertEntityListener;
import com.haulmont.cuba.core.listener.BeforeUpdateEntityListener;
import com.haulmont.cuba.testmodel.jpa_cascade.JpaCascadeFoo;
import java.util.ArrayList;
import java.util.List;
public class TestJpaCascadeFooListener implements
BeforeInsertEntityListener<JpaCascadeFoo>, BeforeUpdateEntityListener<JpaCascadeFoo> {
static List<String> messages = new ArrayList<>();
@Override
public void onBeforeInsert(JpaCascadeFoo entity, EntityManager entityManager) {
messages.add("onBeforeInsert " + entity);
}
@Override
public void onBeforeUpdate(JpaCascadeFoo entity, EntityManager entityManager) {
messages.add("onBeforeUpdate " + entity);
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.jpa_cascade;
import com.haulmont.cuba.core.EntityManager;
import com.haulmont.cuba.core.listener.BeforeInsertEntityListener;
import com.haulmont.cuba.core.listener.BeforeUpdateEntityListener;
import com.haulmont.cuba.testmodel.jpa_cascade.JpaCascadeItem;
import java.util.ArrayList;
import java.util.List;
public class TestJpaCascadeItemListener implements
BeforeInsertEntityListener<JpaCascadeItem>, BeforeUpdateEntityListener<JpaCascadeItem> {
static List<String> messages = new ArrayList<>();
@Override
public void onBeforeInsert(JpaCascadeItem entity, EntityManager entityManager) {
messages.add("onBeforeInsert " + entity);
}
@Override
public void onBeforeUpdate(JpaCascadeItem entity, EntityManager entityManager) {
messages.add("onBeforeUpdate " + entity);
}
}