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

Add support for Hibernate5 #12

Merged
merged 5 commits into from
Oct 19, 2015
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
1 change: 1 addition & 0 deletions elide-dbmanager/elide-dbmanager-hibernate5/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
103 changes: 103 additions & 0 deletions elide-dbmanager/elide-dbmanager-hibernate5/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<!--
~ Copyright 2015, Yahoo Inc.
~ Licensed under the Apache License, Version 2.0
~ See LICENSE file in project root for terms.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>elide-dbmanager-hibernate5</artifactId>
<packaging>jar</packaging>
<name>Elide Hibernate5 Database Manager</name>
<description>Elide database manager for Hibernate5 support</description>
<url>https://github.com/yahoo/elide</url>
<parent>
<groupId>com.yahoo.elide</groupId>
<artifactId>elide-dbmanager-parent-pom</artifactId>
<version>1.0.0.1-SNAPSHOT</version>
</parent>

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to add some fields here for maven central compliance. See: #13

You can probably just copy all of the blocks directly. Namely, you need the following:

  • JavaDoc and Sources (just include plugins in section)
  • Signed artifacts (this is done for us by bintray)
  • Add top-level URL
  • Add license block
  • Add developer block
  • Add scm block

<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>

<developers>
<developer>
<name>Yahoo Inc.</name>
<url>https://github.com/yahoo</url>
</developer>
</developers>

<scm>
<developerConnection>scm:git:ssh://git@github.com/yahoo/elide.git</developerConnection>
<url>https://github.com/yahoo/elide.git</url>
<tag>HEAD</tag>
</scm>

<dependencies>
<dependency>
<groupId>com.yahoo.elide</groupId>
<artifactId>elide-core</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.2.Final</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>xml-apis</artifactId>
<groupId>xml-apis</groupId>
</exclusion>
<exclusion>
<artifactId>jasper-runtime</artifactId>
<groupId>tomcat</groupId>
</exclusion>
<exclusion>
<artifactId>xerces</artifactId>
<groupId>xerces</groupId>
</exclusion>
<exclusion>
<artifactId>jasper-compiler</artifactId>
<groupId>tomcat</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- For proper serialization in elide-core -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.6.3</version>
</dependency>
<!-- Test deps -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
/*
* Copyright 2015, Yahoo Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package com.yahoo.elide.dbmanagers.hibernate5;

import com.google.common.base.Preconditions;
import com.google.common.collect.Iterators;
import com.yahoo.elide.core.DatabaseManager;
import com.yahoo.elide.core.DatabaseTransaction;
import com.yahoo.elide.core.EntityDictionary;
import com.yahoo.elide.core.FilterScope;
import com.yahoo.elide.core.RequestScope;
import com.yahoo.elide.core.exceptions.TransactionException;
import com.yahoo.elide.security.Check;
import com.yahoo.elide.security.CriteriaCheck;
import com.yahoo.elide.security.User;

import org.hibernate.EntityMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;

import java.io.IOException;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;

import lombok.NonNull;
import org.hibernate.resource.transaction.spi.TransactionStatus;

/**
* Hibernate interface library
*/
public class HibernateManager extends DatabaseManager {

/**
* Wraps ScrollableResult as Iterator
* @param <T> type of return object
*/
public static class ScrollableIterator<T> implements Iterable<T>, Iterator<T> {

final private ScrollableResults scroll;
private boolean inUse = false;
private boolean hasNext;

public ScrollableIterator(ScrollableResults scroll) {
this.scroll = scroll;
}

@Override
public Iterator<T> iterator() {
if (inUse) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section concerns me a little bit (probably the same as hibernate3)? What happens if we have two threads A and B with a simultaneous access to iterator. That is, consider if threads A and B concurrently execute lines 64 - 68.

In such a scenario we wouldn't detect the concurrent modification. It feels like the inUse variable is a critical section to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. This file is copied and pasted from Hibernate3. I just modified it to work with Hibernate5.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a follow-up: after talking in person, I think Shad has a good point here. Perhaps-- for performance-- we can simply throw the exception iff we detect the issue (i.e. this shouldn't be used concurrently), but we don't make any strong guarantees about detection.

throw new ConcurrentModificationException();
}

if (!scroll.first()) {
return Collections.emptyListIterator();
}

inUse = true;
hasNext = true;
return Iterators.unmodifiableIterator(this);
}

@Override
public boolean hasNext() {
return hasNext;

}

@Override
public @NonNull T next() {
@SuppressWarnings("unchecked")
@NonNull T row = (T) scroll.get()[0];
Preconditions.checkNotNull(row);
hasNext = scroll.next();
return row;
}
}

/**
* Hibernate Transaction implementation
*/
public class HibernateTransaction implements DatabaseTransaction {

private Transaction transaction;
private LinkedHashSet<Runnable> deferredTasks = new LinkedHashSet<>();

/**
* Instantiates a new Hibernate transaction.
*
* @param transaction the transaction
*/
public HibernateTransaction(Transaction transaction) {
this.transaction = transaction;
}

@Override
public void delete(Object object) {
deferredTasks.add(() -> getSession().delete(object));
}

@Override
public void save(Object object) {
deferredTasks.add(() -> getSession().saveOrUpdate(object));
}

@Override
public void flush() {
try {
for (Runnable task : deferredTasks) {
task.run();
}
deferredTasks.clear();
getSession().flush();
} catch (HibernateException e) {
throw new TransactionException(e);
}
}

@Override
public void commit() {
try {
this.flush();
this.transaction.commit();
} catch (HibernateException e) {
throw new TransactionException(e);
}
}

@Override
public <T> T createObject(Class<T> entityClass) {
try {
T object = entityClass.newInstance();
deferredTasks.add(() -> getSession().persist(object));
return object;
} catch (InstantiationException | IllegalAccessException e) {
return null;
}
}

@Override
public <T> T loadObject(Class<T> loadClass, String id) {
@SuppressWarnings("unchecked")
T record = (T) getSession().load(loadClass, Long.valueOf(id));
try {
Hibernate.initialize(record);
} catch (ObjectNotFoundException e) {
return null;
}
return record;
}

@Override
public <T> Iterable<T> loadObjects(Class<T> loadClass) {
@SuppressWarnings("unchecked")
Iterable<T> list = new ScrollableIterator(getSession().createCriteria(loadClass)
.scroll(ScrollMode.FORWARD_ONLY));
return list;
}
@Override
public <T> Iterable<T> loadObjects(Class<T> loadClass, FilterScope<T> filterScope) {
Criterion criterion = buildCriterion(filterScope);

// if no criterion then return all objects
if (criterion == null) {
return loadObjects(loadClass);
}

@SuppressWarnings("unchecked")
Iterable<T> list = new ScrollableIterator(getSession().createCriteria(loadClass)
.add(criterion)
.scroll(ScrollMode.FORWARD_ONLY));
return list;
}

/**
* builds criterion if all checks implement CriteriaCheck
* @param <T> Filter type
* @param filterScope Filter Scope
*/
public <T> Criterion buildCriterion(FilterScope<T> filterScope) {
Criterion compositeCriterion = null;
List<Check<T>> checks = filterScope.getChecks();
RequestScope requestScope = filterScope.getRequestScope();
for (Check check : checks) {
Criterion criterion;
if (check instanceof CriteriaCheck) {
criterion = ((CriteriaCheck) check).getCriterion(requestScope);
} else {
criterion = null;
}

// if no criterion, examine userPermission and ANY state
if (criterion == null) {
switch (filterScope.getRequestScope().getUser().checkUserPermission(check)) {
// ALLOW and ALL try more criteria
case ALLOW:
if (!filterScope.isAny()) {
continue;
}
break;

// DENY and ANY check try more criteria
case DENY:
if (filterScope.isAny()) {
continue;
}
break;
}

// Otherwise no criteria filtering possible
return null;
} else if (compositeCriterion == null) {
compositeCriterion = criterion;
} else if (filterScope.isAny()) {
compositeCriterion = Restrictions.or(compositeCriterion, criterion);
} else {
compositeCriterion = Restrictions.and(compositeCriterion, criterion);
}
}
return compositeCriterion;
}

@Override
public void close() throws IOException {
if (this.transaction.getStatus() == TransactionStatus.ACTIVE) {
transaction.rollback();
throw new IOException("Transaction not closed");
}
}

@Override
public User accessUser(Object opaqueUser) {
return new User(opaqueUser);
}
}

private final SessionFactory sessionFactory;

/**
* Initialize HibernateManager and dictionaries
*
* @param aSessionFactory the a session factory
*/
public HibernateManager(SessionFactory aSessionFactory) {
this.sessionFactory = aSessionFactory;
}

@Override
public void populateEntityDictionary(EntityDictionary dictionary) {
/* bind all entities */
for (ClassMetadata meta : sessionFactory.getAllClassMetadata().values()) {
dictionary.bindEntity(meta.getMappedClass());
}
}

/**
* Get current Hibernate session
*
* @return session
*/
public Session getSession() {
Session session = sessionFactory.getCurrentSession();
Preconditions.checkNotNull(session);
Preconditions.checkArgument(session.isConnected());
return session;
}

/**
* Start Hibernate transaction
*
* @return transaction
*/
@Override
public DatabaseTransaction beginTransaction() {
Session session = sessionFactory.getCurrentSession();
Preconditions.checkNotNull(session);
return new HibernateTransaction(session.beginTransaction());
}
}
Loading