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

Added functionality for non-autoincremented id fields. #10

Merged
merged 1 commit into from
Mar 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2018 OmniFaces.
*
* 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.omnifaces.persistence.model;

import static javax.persistence.GenerationType.IDENTITY;

import java.io.Serializable;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

import org.omnifaces.persistence.service.BaseEntityService;

/**
* <p>
* Let all your entities that autogenerate their Ids extend from this. Then you can make use of {@link BaseEntityService}. This mapped superclass
* already automatically takes care of the <code>id</code> column.
* <p>
* There are two more mapped superclasses which may also be of interest.
* <ul>
* <li>{@link TimestampedEntity} - extends {@link BaseAutoIdEntity} with <code>created</code> and <code>lastModified</code>
* columns and automatically takes care of them.
* <li>{@link VersionedEntity} - extends {@link TimestampedEntity} with a <code>@Version</code> column.
* </ul>
*
* @param <I> The generic ID type, usually {@link Long}.
* @author Bauke Scholtz
*/

@MappedSuperclass
public abstract class BaseAutoIdEntity<I extends Comparable<I> & Serializable> extends BaseEntity<I> {

private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy = IDENTITY)
private I id;

@Override
public I getId() {
return id;
}

@Override
public void setId(I id) {
this.id = id;
}

}
48 changes: 24 additions & 24 deletions src/main/java/org/omnifaces/persistence/model/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
/*
* Copyright 2018 OmniFaces.
*
* 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.omnifaces.persistence.model;

import static javax.persistence.GenerationType.IDENTITY;

import java.io.Serializable;
import java.util.Objects;

import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

import org.omnifaces.persistence.listener.BaseEntityListener;
import org.omnifaces.persistence.service.BaseEntityService;

/**
* <p>
* Let all your entities extend from this. Then you can make use of {@link BaseEntityService}. This mapped superclass
* already automatically takes care of the <code>id</code> column.
* Let all your entities that don't auto-generate ids extend from this. Then you can make use of {@link BaseEntityService}.
* Entity Id field and its accessors must be implemented in subclasses. It can also be used as a base class to provide for
* <code>@GeneratedValue(strategy = SEQUENCE)</code> (i.e. other than IDENTITY functionality).
* <p>
* There are two more mapped superclasses which may also be of interest.
* <ul>
* <li>{@link TimestampedEntity} - extends {@link BaseEntity} with <code>created</code> and <code>lastModified</code>
* columns and automatically takes care of them.
* <li>{@link VersionedEntity} - extends {@link TimestampedEntity} with a <code>@Version</code> column.
* </ul>
* See {@link BaseAutoIdEntity} class for a base to extend the entities with Id to be set automatically.
*
* @param <I> The generic ID type, usually {@link Long}.
* @param <I> The generic ID type, usually {@link String}.
* @author Bauke Scholtz
*/
@MappedSuperclass
Expand All @@ -34,18 +41,11 @@ public abstract class BaseEntity<I extends Comparable<I> & Serializable> impleme

private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy = IDENTITY)
private I id;
@Override
public abstract I getId();

@Override
public I getId() {
return id;
}

@Override
public void setId(I id) {
this.id = id;
}
@Override
public abstract void setId(I id);

/**
* Hashes by default the classname and ID.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* @author Bauke Scholtz
*/
@MappedSuperclass
public abstract class TimestampedEntity<I extends Comparable<I> & Serializable> extends BaseEntity<I> implements Timestamped {
public abstract class TimestampedEntity<I extends Comparable<I> & Serializable> extends BaseAutoIdEntity<I> implements Timestamped {

private static final long serialVersionUID = 1L;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import static org.omnifaces.utils.stream.Streams.stream;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
Expand Down Expand Up @@ -61,6 +62,7 @@
import javax.persistence.ElementCollection;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.GeneratedValue;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
Expand Down Expand Up @@ -182,6 +184,7 @@ public abstract class BaseEntityService<I extends Comparable<I> & Serializable,

private final Class<I> identifierType;
private final Class<E> entityType;
private boolean shouldSetAutoId;

private Provider provider;
private Database database;
Expand All @@ -205,7 +208,24 @@ public BaseEntityService() {
Entry<Class<?>, Class<?>> typeMapping = TYPE_MAPPINGS.computeIfAbsent(getClass(), BaseEntityService::computeTypeMapping);
identifierType = (Class<I>) typeMapping.getKey();
entityType = (Class<E>) typeMapping.getValue();
}

shouldSetAutoId = autoIdPresent();
}

private boolean autoIdPresent() {
Class<?> tmp = entityType;
Field fld = null;
do {
try {
Field f = tmp.getDeclaredField("id");
fld = f;
break;
} catch (NoSuchFieldException e) {
tmp = tmp.getSuperclass();
}
} while (tmp != null);
return fld == null ? false : fld.isAnnotationPresent(GeneratedValue.class);
}

/**
* The postconstruct initializes the element and one-to-many collections.
Expand Down Expand Up @@ -419,12 +439,14 @@ public List<E> getAll() {
* Persist given entity. Any bean validation constraint violation will be logged separately.
* @param entity Entity to persist.
* @return Entity ID.
* @throws IllegalEntityStateException When entity has an ID.
* @throws IllegalEntityStateException When entity has an ID and it should be autogenerated.
*/
public I persist(E entity) {
if (entity.getId() != null) {
throw new IllegalEntityStateException(entity, "Entity has an ID. Use update() instead.");
}
if (shouldSetAutoId) {
if (entity.getId() != null) {
throw new IllegalEntityStateException(entity, "Entity has an ID. Use update() instead.");
}
}

try {
getEntityManager().persist(entity);
Expand All @@ -445,12 +467,14 @@ public I persist(E entity) {
* beyond the scope of this method.
* @param entity Entity to update.
* @return Updated entity.
* @throws IllegalEntityStateException When entity has no ID.
* @throws IllegalEntityStateException When entity has no ID and it should be autogenerated.
*/
public E update(E entity) {
if (entity.getId() == null) {
throw new IllegalEntityStateException(entity, "Entity has no ID. Use persist() instead.");
}
if (shouldSetAutoId) {
if (entity.getId() == null) {
throw new IllegalEntityStateException(entity, "Entity has no ID. Use persist() instead.");
}
}

if (validator != null) {
// EntityManager#merge() doesn't directly throw ConstraintViolationException without performing flush, so we can't put it in a
Expand Down