mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
NEW: Ebean.checkUniqueness - you can check a bean, if save would work (#1303)
This commit is contained in:
committed by
Rob Bygrave
parent
0c368ed7b3
commit
efe325034b
@@ -3,11 +3,13 @@ package io.ebean;
|
||||
import io.ebean.annotation.TxIsolation;
|
||||
import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
@@ -15,6 +17,7 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -116,7 +119,7 @@ public final class Ebean {
|
||||
static {
|
||||
EbeanVersion.getVersion(); // initalizes the version class and logs the version.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Manages creation and cache of EbeanServers.
|
||||
*/
|
||||
@@ -641,6 +644,30 @@ public final class Ebean {
|
||||
return serverMgr.getDefaultServer().saveAll(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks the uniqueness of a bean. I.e. if the save will work. It will return the
|
||||
* properties that violates an unique / primary key. This may be done in an UI save action to
|
||||
* validate if the user has entered correct values.
|
||||
*
|
||||
* Note: This method queries the DB for uniqueness of all indices, so do not use it in a batch update.
|
||||
*
|
||||
* TODO: it checks only the root bean!
|
||||
* @param bean
|
||||
* @return a set of Properties if constraint validation was detected or empty list.
|
||||
*/
|
||||
@Nonnull
|
||||
public static Set<Property> checkUniqueness(Object bean) {
|
||||
return serverMgr.getDefaultServer().checkUniqueness(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #checkUniqueness(Object)}. but with given transaction.
|
||||
*/
|
||||
@Nonnull
|
||||
public static Set<Property> checkUniqueness(Object bean, Transaction transaction) {
|
||||
return serverMgr.getDefaultServer().checkUniqueness(bean, transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the bean.
|
||||
* <p>
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.ebean.annotation.TxIsolation;
|
||||
import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
@@ -1461,6 +1462,26 @@ public interface EbeanServer {
|
||||
*/
|
||||
int saveAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* This method checks the uniqueness of a bean. I.e. if the save will work. It will return the
|
||||
* properties that violates an unique / primary key. This may be done in an UI save action to
|
||||
* validate if the user has entered correct values.
|
||||
*
|
||||
* Note: This method queries the DB for uniqueness of all indices, so do not use it in a batch update.
|
||||
*
|
||||
* TODO: it checks only the root bean!
|
||||
* @param bean
|
||||
* @return a set of Properties if constraint validation was detected or empty list.
|
||||
*/
|
||||
@Nonnull
|
||||
Set<Property> checkUniqueness(Object bean);
|
||||
|
||||
/**
|
||||
* Same as {@link #checkUniqueness(Object)}. but with given transaction.
|
||||
*/
|
||||
@Nonnull
|
||||
Set<Property> checkUniqueness(Object bean, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Marks the entity bean as dirty.
|
||||
* <p>
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebean.BeanState;
|
||||
import io.ebean.CallableSql;
|
||||
import io.ebean.DocumentStore;
|
||||
import io.ebean.ExpressionFactory;
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.Filter;
|
||||
import io.ebean.FutureIds;
|
||||
import io.ebean.FutureList;
|
||||
@@ -49,6 +50,7 @@ import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
@@ -104,7 +106,10 @@ import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -2109,4 +2114,78 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
return transactionManager.collectTransactionStatistics(reset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Property> checkUniqueness(Object bean) {
|
||||
return checkUniqueness(bean, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Property> checkUniqueness(Object bean, Transaction transaction) {
|
||||
|
||||
EntityBean entityBean = checkEntityBean(bean);
|
||||
BeanDescriptor<?> beanDesc = getBeanDescriptor(entityBean.getClass());
|
||||
|
||||
BeanProperty idProperty = beanDesc.getIdProperty();
|
||||
// if the ID of the Property is null we are unable to check uniqueness
|
||||
if (idProperty == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Object id = idProperty.getVal(entityBean);
|
||||
if (entityBean._ebean_intercept().isNew() && id != null) {
|
||||
// Primary Key is changeable only on new models - so skip check if we are not
|
||||
// new.
|
||||
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
|
||||
query.setId(id);
|
||||
if (findCount(query, transaction) > 0) {
|
||||
Set<Property> ret = new HashSet<>();
|
||||
ret.add(idProperty);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (BeanProperty[] props : beanDesc.getUniqueProps()) {
|
||||
Set<Property> ret = checkUniqueness(entityBean, beanDesc, props, transaction);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a set of properties if saving the bean will violate the unique constraints
|
||||
* (definded by given properties).
|
||||
*/
|
||||
private Set<Property> checkUniqueness(EntityBean entityBean, BeanDescriptor<?> beanDesc, BeanProperty[] props,
|
||||
Transaction transaction) {
|
||||
BeanProperty idProperty = beanDesc.getIdProperty();
|
||||
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
|
||||
ExpressionList<?> exprList = query.where();
|
||||
|
||||
if (!entityBean._ebean_intercept().isNew()) {
|
||||
// if model is not new, exclude ourself.
|
||||
exprList.ne(idProperty.getName(), idProperty.getVal(entityBean));
|
||||
}
|
||||
|
||||
for (Property prop : props) {
|
||||
Object value = prop.getVal(entityBean);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
exprList.eq(prop.getName(), value);
|
||||
}
|
||||
|
||||
if (findCount(query, transaction) > 0) {
|
||||
Set<Property> ret = new LinkedHashSet<>();
|
||||
for (Property prop : props) {
|
||||
ret.add(prop);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -350,6 +350,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
protected final BeanProperty[] propertiesIndex;
|
||||
private final BeanProperty[] propertiesGenInsert;
|
||||
private final BeanProperty[] propertiesGenUpdate;
|
||||
private final List<BeanProperty[]> propertiesUnique = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The bean class name or the table name for MapBeans.
|
||||
@@ -769,9 +770,43 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
* Perform last initialisation for the descriptor.
|
||||
*/
|
||||
public void initLast() {
|
||||
|
||||
for (BeanProperty prop : propertiesNonTransient) {
|
||||
if (prop.isUnique()) {
|
||||
propertiesUnique.add(new BeanProperty[] { prop });
|
||||
}
|
||||
}
|
||||
// convert unique columns to properties
|
||||
if (indexDefinitions != null) {
|
||||
for (IndexDefinition indexDef : indexDefinitions) {
|
||||
if (indexDef.isUnique()) {
|
||||
addUniqueColumns(indexDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
docStoreEmbeddedInvalidation = docStoreAdapter.hasEmbeddedInvalidation();
|
||||
}
|
||||
|
||||
private void addUniqueColumns(IndexDefinition indexDef) {
|
||||
String[] cols = indexDef.getColumns();
|
||||
BeanProperty[] props = new BeanProperty[cols.length];
|
||||
for (int i = 0; i < cols.length; i++) {
|
||||
String propName = findBeanPath("", cols[i]);
|
||||
if (propName == null) {
|
||||
return;
|
||||
}
|
||||
props[i] = findBeanProperty(propName);
|
||||
}
|
||||
if (props.length == 1) {
|
||||
for (BeanProperty[] inserted : propertiesUnique) {
|
||||
if (inserted.length == 1 && inserted[0].equals(props[0])) {
|
||||
return; // do not insert duplicates
|
||||
}
|
||||
}
|
||||
}
|
||||
propertiesUnique.add(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the document mapping.
|
||||
*/
|
||||
@@ -3220,4 +3255,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return jsonHelp.jsonReadObject(jsonRead, path);
|
||||
}
|
||||
|
||||
public List<BeanProperty[]> getUniqueProps() {
|
||||
return propertiesUnique;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user