CommonNestedSetBean.java
package sk.iway.iwcm.database.nestedsets;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.LockModeType;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
import javax.persistence.TypedQuery;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.eclipse.persistence.jpa.JpaEntityManager;
import sk.iway.iwcm.database.ActiveRecord;
import sk.iway.iwcm.database.DataSource;
import sk.iway.iwcm.database.nestedsets.annotations.LeftColumn;
import sk.iway.iwcm.database.nestedsets.annotations.LevelColumn;
import sk.iway.iwcm.database.nestedsets.annotations.RightColumn;
import sk.iway.iwcm.database.nestedsets.annotations.RootColumn;
import sk.iway.iwcm.system.jpa.JpaTools;
@MappedSuperclass
abstract public class CommonNestedSetBean<T extends NodeInfo> extends ActiveRecord implements NodeInfo
{
public static List<String> TO_STRING_IGNORED_PROPERTIES = Arrays.asList("parent", "ancestors", "children", "descendants");
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@Column(updatable=false)
@LeftColumn
private int lft;
@RightColumn
@Column(updatable=false)
private int rgt;
@LevelColumn
@Column(updatable=false, name="lvl")
private int level;
@RootColumn
private int rootId;
@Transient
protected T parent = null;
@Transient
protected boolean createRoot = false;
@Override
public int getId()
{
return id;
}
@Override
public void setId(int id)
{
this.id = id;
}
@Override
@JsonIgnore
public int getLeftValue()
{
return lft;
}
@Override
@JsonIgnore
public int getRightValue()
{
return rgt;
}
@Override
// @JsonIgnore
public int getLevel()
{
return level;
}
@Override
@JsonIgnore
public int getRootValue()
{
return rootId;
}
@Override
public void setLeftValue(int value)
{
lft = value;
}
@Override
public void setRightValue(int value)
{
rgt = value;
}
@Override
public void setLevel(int level)
{
this.level = level;
}
@Override
public void setRootValue(int value)
{
rootId = value;
}
@Override
@SuppressWarnings("unchecked")
public boolean save()
{
String dbName = "iwcm";
if (this.getClass().isAnnotationPresent(DataSource.class)) {
Annotation annotation = this.getClass().getAnnotation(DataSource.class);
DataSource dataSource = (DataSource) annotation;
dbName = dataSource.name();
}
JpaEntityManager em = JpaTools.getEclipseLinkEntityManager(dbName);
if (this.id>0)
{
boolean commitAfterSuccess = false;
if (!em.getTransaction().isActive())
{
commitAfterSuccess = true;
em.getTransaction().begin();
}
try
{
em.merge(this);
if (commitAfterSuccess)
{
em.getTransaction().commit();
}
return true;
}
catch (Exception e) {sk.iway.iwcm.Logger.error(e);}
}
else
{
if (parent==null && !createRoot) throw new IllegalStateException("'parent' must be specified!");
if (parent!=null)
{
JpaNode<T> parentJpaNode = new JpaNode<T>(parent, new JpaNestedSetManager(em));
try
{
boolean commitAfterSuccess = false;
if (!em.getTransaction().isActive())
{
commitAfterSuccess = true;
em.getTransaction().begin();
}
parentJpaNode.addChild((T)this);
if (commitAfterSuccess)
{
em.getTransaction().commit();
}
parentJpaNode = null;
return true;
}
catch (Exception e) { sk.iway.iwcm.Logger.error(e);}
parent = null;
}
else
{
JpaNestedSetManager nsm = new JpaNestedSetManager(em);
boolean commitAfterSuccess = false;
if (!em.getTransaction().isActive())
{
commitAfterSuccess = true;
em.getTransaction().begin();
}
nsm.createRoot(this);
if (commitAfterSuccess)
{
em.getTransaction().commit();
}
return true;
}
}
return false;
}
@Override
public boolean delete()
{
String dbName = "iwcm";
if (this.getClass().isAnnotationPresent(DataSource.class)) {
Annotation annotation = this.getClass().getAnnotation(DataSource.class);
DataSource dataSource = (DataSource) annotation;
dbName = dataSource.name();
}
JpaEntityManager em = JpaTools.getEclipseLinkEntityManager(dbName);
JpaNestedSetManager nsm = new JpaNestedSetManager(em);
updateNestedSetProperties();
@SuppressWarnings({ "unchecked" })
JpaNode<T> node = new JpaNode<T>((T)this, nsm);
// MBO: povolime vymazanie celej vetvy - uzdela aj s podstromom
// if (node.hasChildren()) throw new IllegalStateException("Node cannot be deleted, because has children!");
try
{
boolean commitAfterSuccess = false;
if (!em.getTransaction().isActive())
{
commitAfterSuccess = true;
em.getTransaction().begin();
}
node.delete();
if (commitAfterSuccess)
{
em.getTransaction().commit();
}
return true;
}
catch (Exception e)
{
sk.iway.iwcm.Logger.error(e);
}
return false;
}
public void setParent(T parent)
{
this.parent = parent;
}
public void createRoot()
{
this.createRoot = true;
}
private T unwrapped(Node<T> node)
{
if (node!=null) return node.unwrap();
return null;
}
private List<T> unwrappedList(List<Node<T>> nodeList)
{
if (nodeList!=null)
{
List<T> result = new ArrayList<>();
for (Node<T> node : nodeList)
{
result.add(node.unwrap());
}
return result;
}
return null;
}
private JpaEntityManager getEm()
{
String dbName = "iwcm";
if (this.getClass().isAnnotationPresent(DataSource.class)) {
Annotation annotation = this.getClass().getAnnotation(DataSource.class);
DataSource dataSource = (DataSource) annotation;
dbName = dataSource.name();
}
return JpaTools.getEclipseLinkEntityManager(dbName);
}
private JpaNestedSetManager getNsm()
{
return new JpaNestedSetManager(getEm());
}
/**
* update kritickych hodnot na cerstve z DB
*/
@SuppressWarnings("unchecked")
private void updateNestedSetProperties()
{
updateNestedSetProperties((T)this);
}
private void updateNestedSetProperties(T bean)
{
StringBuilder jpql = new StringBuilder();
jpql.append("SELECT NEW sk.iway.iwcm.database.nestedsets.CommonNestedSetBean.SimpleNestedSetItem(t.lft, t.rgt, t.level) FROM ").append(this.getClass().getSimpleName()).append(" t WHERE t.id=?1");
TypedQuery<SimpleNestedSetItem> typedQuery = getEm().createQuery(jpql.toString() , SimpleNestedSetItem.class);
typedQuery.setParameter(1, bean.getId());
SimpleNestedSetItem item=typedQuery.getSingleResult();
if (item==null) throw new IllegalStateException("Entity not exist.");
bean.setLeftValue(item.left);
bean.setRightValue(item.right);
bean.setLevel(item.level);
item=null;
}
@JsonIgnore
public T getParent()
{
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> node = new JpaNode(this, getNsm());
return unwrapped(node.getParent());
}
@JsonIgnore
public List<T> getAncestors()
{
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> node = new JpaNode(this, getNsm());
return unwrappedList(node.getAncestors());
}
@JsonIgnore
public List<T> getChildren()
{
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> node = new JpaNode(this, getNsm());
return unwrappedList(node.getChildren());
}
@JsonIgnore
public List<T> getDescendants()
{
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> node = new JpaNode(this, getNsm());
return unwrappedList(node.getDescendants());
}
public T addChild(T child)
{
updateNestedSetProperties();
JpaNestedSetManager nsm = getNsm();
nsm.getEntityManager().getTransaction().begin();
try
{
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> node = new JpaNode(this, nsm);
return unwrapped(node.addChild(child));
}
finally
{
nsm.getEntityManager().getTransaction().commit();
updateNestedSetProperties();
}
}
public boolean isAncestorOf(T maybeDescentan)
{
updateNestedSetProperties();
updateNestedSetProperties(maybeDescentan);
JpaNestedSetManager nsm = getNsm();
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> node = new JpaNode(this, nsm);
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> maybeDescentanNode = new JpaNode(maybeDescentan, nsm);
return maybeDescentanNode.isDescendantOf(node);
}
public boolean isDescentantOf(T maybeAncestor)
{
updateNestedSetProperties();
updateNestedSetProperties(maybeAncestor);
JpaNestedSetManager nsm = getNsm();
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> node = new JpaNode(this, nsm);
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> maybeAncestorNode = new JpaNode(maybeAncestor, nsm);
return node.isDescendantOf(maybeAncestorNode);
}
public void moveToNewParent(T parent)
{
updateNestedSetProperties();
updateNestedSetProperties(parent);
JpaNestedSetManager nsm = getNsm();
nsm.getEntityManager().getTransaction().begin();
//@Experimental
nsm.getEntityManager().lock(parent, LockModeType.PESSIMISTIC_READ);
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> node = new JpaNode(this, nsm);
@SuppressWarnings({ "rawtypes", "unchecked" })
Node<T> parentNode = new JpaNode(parent, nsm);
node.moveAsLastChildOf(parentNode);
nsm.getEntityManager().getTransaction().commit();
updateNestedSetProperties();
updateNestedSetProperties(parent);
}
public static class SimpleNestedSetItem
{
protected int left;
protected int right;
protected int level;
public SimpleNestedSetItem(int left, int right, int level)
{
super();
this.left = left;
this.right = right;
this.level = level;
}
}
}