org.babyfish.hibernate.collection.spi.persistence.SetBasePersistence Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of babyfish-hibernate Show documentation
Show all versions of babyfish-hibernate Show documentation
The hibernate enhancement project of BabyFish.
The newest version!
/*
* BabyFish, Object Model Framework for Java and JPA.
* https://github.com/babyfish-ct/babyfish
*
* Copyright (c) 2008-2015, Tao Chen
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* Please visit "http://opensource.org/licenses/LGPL-3.0" to know more.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*/
package org.babyfish.hibernate.collection.spi.persistence;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.babyfish.collection.ArrayList;
import org.babyfish.collection.HashMap;
import org.babyfish.collection.MACollections;
import org.babyfish.collection.MASet;
import org.babyfish.collection.TreeMap;
import org.babyfish.collection.UnifiedComparator;
import org.babyfish.collection.XCollection;
import org.babyfish.collection.XMap;
import org.babyfish.collection.XSet;
import org.babyfish.collection.spi.laziness.QueuedOperationType;
import org.babyfish.lang.Arguments;
import org.babyfish.lang.IllegalProgramException;
import org.babyfish.lang.Ref;
import org.babyfish.util.LazyResource;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.internal.JoinHelper;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.type.Type;
/**
* @author Tao Chen
*/
public abstract class SetBasePersistence extends AbstractBasePersistence {
private static final long serialVersionUID = 4884987773691896886L;
private static final LazyResource LAZY_COMMON_RESOURCE =
LazyResource.of(CommonResource.class);
private static final String MIDDLE_TABLE_ALIAS = "babyfish_mt_alias_";
private transient List tempList;
protected SetBasePersistence() {
}
protected abstract void setBase(MASet base);
protected abstract MASet getBase();
protected abstract UnifiedComparator super E> unifiedComparator();
protected abstract XMap getQueuedOperations();
protected abstract void performQueuedOperations();
@SuppressWarnings("unchecked")
@Override
public Serializable getSnapshot(CollectionPersister persister) throws HibernateException {
XSet baseSet = this.getBase();
UnifiedComparator super E> unifiedComparator = baseSet.unifiedComparator();
Map clonedMap;
if (unifiedComparator.comparator() != null) {
clonedMap = new TreeMap(
unifiedComparator.comparator(),
unifiedComparator.comparator());
} else {
clonedMap = new HashMap(
unifiedComparator.equalityComparator(),
unifiedComparator.equalityComparator(),
baseSet.size() + 1,
1.F);
}
for (E e : baseSet) {
E copied = (E)persister.getElementType().deepCopy(
e,
persister.getFactory());
clonedMap.put(copied, copied);
}
return (Serializable)clonedMap;
}
@SuppressWarnings("unchecked")
@Override
public boolean equalsSnapshot(CollectionPersister persister)
throws HibernateException {
Set baseSet = this.getBase();
Type elementType = persister.getElementType();
java.util.Map sn = (java.util.Map)this.getSnapshot();
if ( sn.size() != baseSet.size() ) {
return false;
}
else {
Iterator iter = baseSet.iterator();
while ( iter.hasNext() ) {
Object test = iter.next();
Object oldValue = sn.get(test);
if (oldValue==null || elementType.isDirty(oldValue, test, this.getSession())) {
return false;
}
}
return true;
}
}
@Override
public boolean needsInserting(Object entry, int i, Type elemType) throws HibernateException {
Map, ?> sn = (Map, ?>)getSnapshot();
Object oldValue = sn.get(entry);
// note that it might be better to iterate the snapshot but this is safe,
// assuming the user implements equals() properly, as required by the Set
// contract!
return oldValue==null || elemType.isDirty( oldValue, entry, this.getSession());
}
@Override
public boolean needsUpdating(Object entry, int i, Type elemType)
throws HibernateException {
return false;
}
@Override
public boolean isSnapshotEmpty(Serializable snapshot) {
return ((Map, ?>)snapshot).isEmpty();
}
@Override
public void beginRead() {
this.setInitializing(true);
this.tempList = new ArrayList(this.unifiedComparator());
}
@SuppressWarnings("unchecked")
@Override
public E readFrom(
ResultSet rs,
CollectionPersister persister,
CollectionAliases descriptor,
Object owner)
throws HibernateException, SQLException {
E element = (E)persister.readElement(
rs,
owner,
descriptor.getSuffixedElementAliases(),
this.getSession());
if (element != null) {
this.tempList.add(element);
}
return element;
}
@Override
public boolean endRead() {
this.getBase().addAll(this.tempList);
this.tempList = null;
this.setInitialized();
return true;
}
@SuppressWarnings("unchecked")
@Override
public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
Object instantiate = persister.getCollectionType().instantiate(anticipatedSize);
if (!(instantiate instanceof MASet>)) {
throw new IllegalProgramException(
LAZY_COMMON_RESOURCE.get().illegalInstantiate(
persister.getCollectionType().getClass(),
MASet.class
)
);
}
this.setBase((MASet)instantiate);
}
@Override
public boolean afterInitialize() {
this.setInitialized();
if (this.hasQueuedOperations()) {
this.performQueuedOperations();
return false;
}
else {
return true;
}
}
@SuppressWarnings("unchecked")
@Override
public void initializeFromCache(
CollectionPersister persister,
Serializable disassembled,
Object owner) throws HibernateException {
Set baseSet = this.getBase();
Serializable[] arr = (Serializable[])disassembled;
int size = arr.length;
this.beforeInitialize( persister, size );
for (int i = 0; i < size; i++ ) {
E element = (E)persister.getElementType().assemble(arr[i], this.getSession(), owner);
if ( element != null ) {
baseSet.add(element);
}
}
}
@Override
public boolean empty() {
return this.getBase().isEmpty();
}
@Override
public Iterator entries(CollectionPersister persister) {
return MACollections.unmodifiable(this.getBase().iterator());
}
@Override
public Serializable disassemble(CollectionPersister persister)
throws HibernateException {
Set baseSet = this.getBase();
Serializable[] result = new Serializable[baseSet.size()];
Iterator iter = baseSet.iterator();
int i=0;
while ( iter.hasNext() ) {
result[i++] = persister.getElementType().disassemble( iter.next(), this.getSession(), null);
}
return result;
}
@Override
public boolean isRowUpdatePossible() {
return false;
}
@Override
public boolean isWrapper(Object collection) {
return this.getBase() == collection;
}
@Override
public Iterator queuedAdditionIterator() {
XMap map = this.getQueuedOperations();
final Iterator> entryIterator = map.entrySet().iterator();
return new Iterator() {
@Override
public boolean hasNext() {
return entryIterator.hasNext();
}
@Override
public E next() {
Entry e = entryIterator.next();
return e.getValue() == QueuedOperationType.ATTACH ? e.getKey() : null;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@SuppressWarnings("unchecked")
@Override
public Iterator> getDeletes(CollectionPersister persister, boolean indexIsFormula)
throws HibernateException {
Set baseSet = this.getBase();
Type elementType = persister.getElementType();
final Map sn = (Map)this.getSnapshot();
List deletes = new ArrayList(this.unifiedComparator(), sn.size());
Iterator iter = sn.keySet().iterator();
while (iter.hasNext()) {
E test = iter.next();
if (!baseSet.contains(test)) {
// the element has been removed from the set
deletes.add(test);
}
}
iter = baseSet.iterator();
while (iter.hasNext()) {
E test = iter.next();
E oldValue = sn.get(test);
if (oldValue!=null && elementType.isDirty(test, oldValue, this.getSession())) {
// the element has changed
deletes.add(oldValue);
}
}
return deletes.iterator();
}
@Override
public Collection getQueuedOrphans(String entityName) {
XMap map = this.getQueuedOperations();
if (!map.isEmpty()) {
XCollection additions = new ArrayList(map.keyUnifiedComparator(), map.size());
XCollection removals = new ArrayList(map.keyUnifiedComparator(), map.size());
for (Entry entry : map.entrySet()) {
if (entry.getValue() == QueuedOperationType.ATTACH) {
additions.add(entry.getKey());
} else {
removals.add(entry.getKey());
}
}
return getOrphans(removals, additions, entityName, this.getSession());
}
else {
return MACollections.emptySet();
}
}
@SuppressWarnings("unchecked")
@Override
public Collection getOrphans(Serializable snapshot, String entityName)
throws HibernateException {
XMap sn = (XMap)snapshot;
return getOrphans(sn.keySet(), this.getBase(), entityName, this.getSession());
}
@SuppressWarnings("unchecked")
@Override
public E getElement(Object entry) {
return (E)entry;
}
@Override
public final boolean entryExists(Object entry, int i) {
return true;
}
@Deprecated
@Override
public final Object getIndex(Object entry, int i, CollectionPersister persister) {
throw new UnsupportedOperationException();
}
@Deprecated
@Override
public final Object getSnapshotElement(Object entry, int i) {
throw new UnsupportedOperationException("Sets don't support updating by element");
}
/**
* This method is used to replace
* "org.hibernate.collection.AbstractPersistentCollection#readElementExistence(Object element)"
* @param element The example element to be read
* @return The ref or readed element
*
* - NonNull: Read successfully, check the value of ref to check the read value is null or not
* - Null: Read failed
*
*/
@SuppressWarnings("unchecked")
public Ref visionallyRead(E element) {
Arguments.mustNotBeNull("element", element);
String role = this.getNonNullRole();
SessionImplementor session = this.getSession();
if (session == null || !session.isOpen() || !session.isConnected()) {
return null;
}
SessionFactoryImplementor sessionFactory = session.getFactory();
QueryableCollection collection = (QueryableCollection)sessionFactory.getCollectionPersister(role);
EntityPersister elementPersister = collection.getElementPersister();
Object elementId = elementPersister.getIdentifier(element, this.getSession());
if (elementId == null) {
return new Ref<>();
}
if (elementPersister
.getEntityMetamodel()
.getIdentifierProperty()
.getUnsavedValue()
.isUnsaved((Serializable)elementId)) {
return new Ref<>();
}
CriteriaImpl criteria = new CriteriaImpl(elementPersister.getEntityName(), session);
/*
* Add the condition of element.
*/
criteria.add(Restrictions.idEq(elementId));
//ownerKey, not ownerId
Object ownerKey = collection.getCollectionType().getKeyOfOwner(this.getOwner(), session);
//In Hibernate, isOneToMany means that there is no middle table
//The @OneToMany of JPA with middle table is consider as many-to-many in Hibernate
if (sessionFactory.getCollectionPersister(role).isOneToMany()) {
String[] joinOwnerColumns = collection.getKeyColumnNames();
StringBuilder sqlBuilder = new StringBuilder();
for (int i = 0; i < joinOwnerColumns.length; i++) {
if (i != 0) {
sqlBuilder.append(" and ");
}
sqlBuilder
.append("{alias}.")
.append(joinOwnerColumns[i])
.append(" = ?");
}
criteria.add(
Restrictions.sqlRestriction(
sqlBuilder.toString(),
ownerKey,
collection.getKeyType()));
} else {
String lhsPropertyName = collection.getCollectionType().getLHSPropertyName();
int lhsPropertyIndex = -1;
if (lhsPropertyName != null) {
String[] propertyNames = collection.getOwnerEntityPersister().getPropertyNames();
for (int i = propertyNames.length - 1; i >= 0; i--) {
if (propertyNames[i].equals(lhsPropertyName)) {
lhsPropertyIndex = i;
break;
}
}
}
String[] lhsColumnNames = JoinHelper.getLHSColumnNames(
collection.getCollectionType(),
lhsPropertyIndex,
(OuterJoinLoadable)elementPersister,
sessionFactory);
String[] joinElementColumnNames = collection.getElementColumnNames();
String[] joinOwnerColumnNames = collection.getKeyColumnNames();
StringBuilder subQueryBuilder = new StringBuilder();
subQueryBuilder
.append("exists(select * from ")
.append(collection.getTableName())
.append(" as ")
.append(MIDDLE_TABLE_ALIAS)
.append(" where ");
for (int i = 0; i < joinElementColumnNames.length; i++) {
if (i != 0) {
subQueryBuilder.append(" and ");
}
subQueryBuilder
.append("{alias}.")
.append(lhsColumnNames[i])
.append(" = ")
.append(MIDDLE_TABLE_ALIAS)
.append('.')
.append(joinElementColumnNames[i]);
}
for (int i = 0; i < joinOwnerColumnNames.length; i++) {
subQueryBuilder
.append(" and ")
.append(MIDDLE_TABLE_ALIAS)
.append(".")
.append(joinOwnerColumnNames[i])
.append(" = ?");
}
subQueryBuilder.append(')');
criteria.add(
Restrictions.sqlRestriction(
subQueryBuilder.toString(),
ownerKey,
collection.getKeyType()));
}
FlushMode oldFlushMode = session.getFlushMode();
session.setFlushMode(FlushMode.MANUAL);
try {
return new Ref<>((E)criteria.uniqueResult());
} finally {
session.setFlushMode(oldFlushMode);
}
}
}