Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uniform handling of JPA and DTO entities in standard list/detail views #2819

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 17 additions & 5 deletions jmix-core/core/src/main/java/io/jmix/core/EntityStates.java
Expand Up @@ -61,9 +61,9 @@ public class EntityStates {
* Determines whether the instance is <em>New</em>, i.e. just created and not stored in database yet.
*
* @param entity entity instance
* @return - true if the instance is a new persistent entity, or if it is actually in Managed state
* @return - true if the instance is a new JPA entity, or if it is actually in Managed state
* but newly-persisted in this transaction <br>
* - true if the instance is a new non-persistent entity never returned from DataManager <br>
* - true if the instance is not a JPA entity <br>
* - false otherwise
* @throws IllegalArgumentException if entity instance is null
*/
Expand All @@ -85,7 +85,7 @@ public boolean isNew(Object entity) {
*
* @param entity entity instance
* @return - true if the instance is managed,<br>
* - false if it is New (and not yet persisted) or Detached, or if it is not a persistent entity
* - false if it is New (and not yet persisted) or Detached, or if it is not a JPA entity
* @throws IllegalArgumentException if entity instance is null
*/
public boolean isManaged(Object entity) {
Expand All @@ -107,7 +107,7 @@ public boolean isManaged(Object entity) {
*
* @param entity entity instance
* @return - true if the instance is detached,<br>
* - false if it is New or Managed, or if it is not a persistent entity
* - false if it is New or Managed, or if it is not a JPA entity
* @throws IllegalArgumentException if entity instance is null
*/
public boolean isDetached(Object entity) {
Expand Down Expand Up @@ -374,7 +374,19 @@ public boolean isDeleted(Object entity) {
}

/**
* Makes a newly constructed object detached. The detached object can be passed to {@code DataManager.commit()} or
* Manages the <em>New</em> state of the entity instance.
*
* @see #isNew(Object)
*/
public void setNew(Object entity, boolean isNew) {
checkNotNullArgument(entity, "entity is null");
EntityPreconditions.checkEntityType(entity);

getUncheckedEntityEntry(entity).setNew(isNew);
}

/**
* Makes a newly constructed object detached. The detached object can be passed to {@code DataManager.save()} or
* to {@code EntityManager.merge()} to save its state to the database.
* <p>If an object with such ID does not exist in the database, a new object will be inserted.
* <p>If the entity is {@code Versioned}, the version attribute should be equal to the latest version existing in
Expand Down
Expand Up @@ -17,11 +17,15 @@
package entity_states

import io.jmix.core.*
import jakarta.persistence.EntityManagerFactory
import org.apache.commons.lang3.RandomStringUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.jdbc.core.JdbcTemplate
import test_support.DataSpec
import test_support.entity.dto.TestNullableIdDto
import test_support.entity.dto.TestUuidDto
import test_support.entity.entity_extension.Client
import test_support.entity.petclinic.Owner
import test_support.entity.sales.Order
import test_support.entity.sales.OrderLineA
import test_support.entity.sales.OrderLineB
Expand All @@ -32,6 +36,8 @@ import test_support.entity.sec.UserRole

class EntityStatesTest extends DataSpec {

@Autowired
Metadata metadata
@Autowired
DataManager dataManager
@Autowired
Expand All @@ -40,10 +46,60 @@ class EntityStatesTest extends DataSpec {
JdbcTemplate jdbcTemplate
@Autowired
FetchPlans fetchPlans
@Autowired
EntityManagerFactory entityManagerFactory

void cleanup() {
jdbcTemplate.update('delete from SALES_ORDER_LINE')
jdbcTemplate.update('delete from SALES_ORDER')
jdbcTemplate.update('delete from APP_OWNER')
}

def "test isNew for JPA entities"() {
when:
def owner = metadata.create(Owner)

then:
entityStates.isNew(owner)

when:
def wasNewInsideTx = transaction.execute {
def em = entityManagerFactory.createEntityManager()
em.persist(owner)
return entityStates.isNew(owner)
}

then:
wasNewInsideTx
!entityStates.isNew(owner)
}

def "test isNew for DTO entities"() {
when:
def uuidDto = metadata.create(TestUuidDto)

then:
uuidDto.id != null
entityStates.isNew(uuidDto)

when:
entityStates.setNew(uuidDto, false)

then:
!entityStates.isNew(uuidDto)

when:
def nullableIdDto = metadata.create(TestNullableIdDto)

then:
nullableIdDto.id == null
entityStates.isNew(nullableIdDto)

when:
entityStates.setNew(nullableIdDto, false)

then:
!entityStates.isNew(nullableIdDto)
}

def "test getCurrentFetchPlan for object graph"() {
Expand Down
@@ -0,0 +1,47 @@
/*
* Copyright 2019 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 test_support.entity.dto;

import io.jmix.core.entity.annotation.JmixId;
import io.jmix.core.metamodel.annotation.JmixEntity;
import io.jmix.core.metamodel.annotation.JmixProperty;

@JmixEntity
public class TestNullableIdDto {

@JmixId
protected Long id;

@JmixProperty
private String name;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2019 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 test_support.entity.dto;

import io.jmix.core.entity.annotation.JmixGeneratedValue;
import io.jmix.core.entity.annotation.JmixId;
import io.jmix.core.metamodel.annotation.JmixEntity;
import io.jmix.core.metamodel.annotation.JmixProperty;

import java.util.UUID;

@JmixEntity
public class TestUuidDto {

@JmixId
@JmixGeneratedValue
protected UUID id;

@JmixProperty
private String name;

public UUID getId() {
return id;
}

public void setId(UUID id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
Expand Up @@ -37,6 +37,8 @@
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.lang.Nullable;

import java.util.Collection;
import java.util.function.Consumer;

@ActionType(RemoveAction.ID)
Expand All @@ -52,6 +54,7 @@ public class RemoveAction<E> extends SecuredListDataComponentAction<RemoveAction
protected String confirmationHeader;
protected Consumer<AfterActionPerformedEvent<E>> afterActionPerformedHandler;
protected Consumer<ActionCancelledEvent<E>> actionCancelledHandler;
protected Consumer<Collection<E>> delegate;

public RemoveAction() {
this(ID);
Expand Down Expand Up @@ -146,6 +149,13 @@ public void setActionCancelledHandler(@Nullable Consumer<ActionCancelledEvent<E>
this.actionCancelledHandler = actionCancelledHandler;
}

/**
* Sets the delegate to be invoked instead of DataManager to remove the entities from a storage.
*/
public void setDelegate(@Nullable Consumer<Collection<E>> delegate) {
this.delegate = delegate;
}

@Override
protected boolean isPermitted() {
return checkRemovePermission() && super.isPermitted();
Expand Down Expand Up @@ -215,6 +225,10 @@ public void execute() {
builder = builder.onCancel(actionCancelledHandler);
}

if (delegate != null) {
builder = builder.withRemoveDelegate(delegate);
}

builder.remove();
}

Expand Down
Expand Up @@ -96,6 +96,9 @@ protected boolean _load() {
list = dataManager.loadList(loadContext);
} else {
list = delegate.apply(loadContext);
if (list == null) {
return false;
}
}

if (dataContext != null) {
Expand Down
Expand Up @@ -77,10 +77,10 @@ public void load() {

LoadContext<E> loadContext = createLoadContext();

if (delegate == null) {
if (!needLoad())
return;
if (!needLoad())
return;

if (delegate == null) {
if (!sendPreLoadEvent(loadContext)) {
return;
}
Expand All @@ -95,6 +95,9 @@ public void load() {
return;
}
entity = delegate.apply(createLoadContext());
if (entity == null) {
return;
}
}

if (dataContext != null) {
Expand Down
Expand Up @@ -84,6 +84,9 @@ public void load() {
list = dataManager.loadValues(loadContext);
} else {
list = delegate.apply(loadContext);
if (list == null) {
return;
}
}

if (dataContext != null) {
Expand Down
Expand Up @@ -86,6 +86,9 @@ public void load() {
}
} else {
result = delegate.apply(loadContext);
if (result == null) {
return;
}
}

container.setItem(result);
Expand Down