-
Notifications
You must be signed in to change notification settings - Fork 230
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
target/ |
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> | ||
|
||
<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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 In such a scenario we wouldn't detect the concurrent modification. It feels like the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()); | ||
} | ||
} |
There was a problem hiding this comment.
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: