diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQL5Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQL5Dialect.java index 6065a7dada..f9802147f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQL5Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQL5Dialect.java @@ -23,8 +23,13 @@ */ package org.hibernate.dialect; +import java.sql.SQLException; import java.sql.Types; +import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; +import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; +import org.hibernate.internal.util.JdbcExceptionHelper; + /** * An SQL dialect for MySQL 5.x specific features. * @@ -43,4 +48,27 @@ protected void registerVarcharTypes() { public boolean supportsColumnCheck() { return false; } + + public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() { + return EXTRACTER; + } + + private static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() { + + public String extractConstraintName(SQLException sqle) { + try { + final int sqlState = Integer.valueOf( JdbcExceptionHelper.extractSqlState( sqle ) ).intValue(); + switch ( sqlState ) { + case 23000: + return extractUsingTemplate( " for key '", "'", sqle.getMessage() ); + default: + return null; + } + } + catch ( NumberFormatException nfe ) { + return null; + } + } + }; + } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java index 0591ded2a5..e4fe606d16 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java @@ -421,7 +421,9 @@ public void register(ResultSet resultSet, Statement statement) { } } if ( statement != null ) { - if ( LOG.isEnabled( Level.WARN ) && !xref.containsKey( statement ) ) { + // Keep this at DEBUG level, rather than warn. Numerous connection pool implementations can return a + // proxy/wrapper around the JDBC Statement, causing excessive logging here. See HHH-8210. + if ( LOG.isEnabled( Level.DEBUG ) && !xref.containsKey( statement ) ) { LOG.unregisteredStatement(); } Set resultSets = xref.get( statement ); @@ -448,7 +450,9 @@ public void release(ResultSet resultSet, Statement statement) { } } if ( statement != null ) { - if ( LOG.isEnabled( Level.WARN ) && !xref.containsKey( statement ) ) { + // Keep this at DEBUG level, rather than warn. Numerous connection pool implementations can return a + // proxy/wrapper around the JDBC Statement, causing excessive logging here. See HHH-8210. + if ( LOG.isEnabled( Level.DEBUG ) && !xref.containsKey( statement ) ) { LOG.unregisteredStatement(); } final Set resultSets = xref.get( statement ); diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java index c83370e8cd..90055d8540 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java @@ -38,6 +38,7 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Queryable; import org.hibernate.sql.JoinFragment; import org.hibernate.sql.JoinType; import org.hibernate.type.CollectionType; @@ -282,19 +283,36 @@ private void dereferenceCollection(CollectionType collectionType, boolean implic String propName = getPath(); FromClause currentFromClause = getWalker().getCurrentFromClause(); - if ( getWalker().getStatementType() != SqlTokenTypes.SELECT && indexed && classAlias == null ) { - // should indicate that we are processing an INSERT/UPDATE/DELETE - // query with a subquery implied via a collection property - // function. Here, we need to use the table name itself as the - // qualification alias. - // TODO : verify this works for all databases... - // TODO : is this also the case in non-"indexed" scenarios? - String alias = getLhs().getFromElement().getQueryable().getTableName(); - columns = getFromElement().toColumns( alias, propertyPath, false, true ); + // determine whether we should use the table name or table alias to qualify the column names... + // we need to use the table-name when: + // 1) the top-level statement is not a SELECT + // 2) the LHS FromElement is *the* FromElement from the top-level statement + // + // there is a caveat here.. if the update/delete statement are "multi-table" we should continue to use + // the alias also, even if the FromElement is the root one... + // + // in all other cases, we should use the table alias + final FromElement lhsFromElement = getLhs().getFromElement(); + if ( getWalker().getStatementType() != SqlTokenTypes.SELECT ) { + if ( isFromElementUpdateOrDeleteRoot( lhsFromElement ) ) { + // at this point we know we have the 2 conditions above, + // lets see if we have the mentioned "multi-table" caveat... + boolean useAlias = false; + if ( getWalker().getStatementType() != SqlTokenTypes.INSERT ) { + final Queryable persister = lhsFromElement.getQueryable(); + if ( persister.isMultiTable() ) { + useAlias = true; + } + } + if ( ! useAlias ) { + final String lhsTableName = lhsFromElement.getQueryable().getTableName(); + columns = getFromElement().toColumns( lhsTableName, propertyPath, false, true ); + } + } } - //We do not look for an existing join on the same path, because - //it makes sense to join twice on the same collection role + // We do not look for an existing join on the same path, because + // it makes sense to join twice on the same collection role FromElementFactory factory = new FromElementFactory( currentFromClause, getLhs().getFromElement(), diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromReferenceNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromReferenceNode.java index 8f1cdc663f..9658c4d65d 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromReferenceNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromReferenceNode.java @@ -27,6 +27,7 @@ import antlr.collections.AST; import org.jboss.logging.Logger; +import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; import org.hibernate.internal.CoreMessageLogger; /** @@ -130,4 +131,15 @@ public FromElement getImpliedJoin() { return null; } + @SuppressWarnings("SimplifiableIfStatement") + protected boolean isFromElementUpdateOrDeleteRoot(FromElement element) { + if ( element.getFromClause().getParentFromClause() != null ) { + // its not even a root... + return false; + } + + return getWalker().getStatementType() == HqlSqlTokenTypes.DELETE + || getWalker().getStatementType() == HqlSqlTokenTypes.UPDATE; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IdentNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IdentNode.java index ff79488f61..119838102b 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IdentNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IdentNode.java @@ -150,42 +150,54 @@ else if (result == COMPONENT_REF) { private boolean resolveAsAlias() { // This is not actually a constant, but a reference to FROM element. - FromElement element = getWalker().getCurrentFromClause().getFromElement( getText() ); - if ( element != null ) { - setType( SqlTokenTypes.ALIAS_REF ); - setFromElement( element ); - String[] columnExpressions = element.getIdentityColumns(); - final boolean isInNonDistinctCount = getWalker().isInCount() && ! getWalker().isInCountDistinct(); - final boolean isCompositeValue = columnExpressions.length > 1; - if ( isCompositeValue ) { - if ( isInNonDistinctCount && ! getWalker().getSessionFactoryHelper().getFactory().getDialect().supportsTupleCounts() ) { - setText( columnExpressions[0] ); - } - else { - String joinedFragment = StringHelper.join( ", ", columnExpressions ); - // avoid wrapping in parenthesis (explicit tuple treatment) if possible due to varied support for - // tuple syntax across databases.. - final boolean shouldSkipWrappingInParenthesis = - getWalker().isInCount() - || getWalker().getCurrentTopLevelClauseType() == HqlSqlTokenTypes.ORDER - || getWalker().getCurrentTopLevelClauseType() == HqlSqlTokenTypes.GROUP; - if ( ! shouldSkipWrappingInParenthesis ) { - joinedFragment = "(" + joinedFragment + ")"; - } - setText( joinedFragment ); - } - return true; - } - else if ( columnExpressions.length > 0 ) { - setText( columnExpressions[0] ); - return true; + final FromElement element = getWalker().getCurrentFromClause().getFromElement( getText() ); + if ( element == null ) { + return false; + } + + setType( SqlTokenTypes.ALIAS_REF ); + setFromElement( element ); + + String[] columnExpressions = element.getIdentityColumns(); + + // determine whether to apply qualification (table alias) to the column(s)... + if ( ! isFromElementUpdateOrDeleteRoot( element ) ) { + if ( StringHelper.isNotEmpty( element.getTableAlias() ) ) { + // apparently we also need to check that they are not already qualified. Ugh! + columnExpressions = StringHelper.qualifyIfNot( element.getTableAlias(), columnExpressions ); } } + + final boolean isInNonDistinctCount = getWalker().isInCount() && ! getWalker().isInCountDistinct(); + final boolean isCompositeValue = columnExpressions.length > 1; + if ( isCompositeValue ) { + if ( isInNonDistinctCount && ! getWalker().getSessionFactoryHelper().getFactory().getDialect().supportsTupleCounts() ) { + setText( columnExpressions[0] ); + } + else { + String joinedFragment = StringHelper.join( ", ", columnExpressions ); + // avoid wrapping in parenthesis (explicit tuple treatment) if possible due to varied support for + // tuple syntax across databases.. + final boolean shouldSkipWrappingInParenthesis = + getWalker().isInCount() + || getWalker().getCurrentTopLevelClauseType() == HqlSqlTokenTypes.ORDER + || getWalker().getCurrentTopLevelClauseType() == HqlSqlTokenTypes.GROUP; + if ( ! shouldSkipWrappingInParenthesis ) { + joinedFragment = "(" + joinedFragment + ")"; + } + setText( joinedFragment ); + } + return true; + } + else if ( columnExpressions.length > 0 ) { + setText( columnExpressions[0] ); + return true; + } + return false; } - private Type getNakedPropertyType(FromElement fromElement) - { + private Type getNakedPropertyType(FromElement fromElement) { if (fromElement == null) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index a3fcc8e3a4..691e79bf1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -1342,7 +1342,9 @@ void unableToWriteCachedFile(String path, @Message(value = "ResultSet had no statement associated with it, but was not yet registered", id = 386) void unregisteredResultSetWithoutStatement(); - @LogMessage(level = WARN) + // Keep this at DEBUG level, rather than warn. Numerous connection pool implementations can return a + // proxy/wrapper around the JDBC Statement, causing excessive logging here. See HHH-8210. + @LogMessage(level = DEBUG) @Message(value = "ResultSet's statement was not registered", id = 387) void unregisteredStatement(); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index 06c44df74d..b0683d6b9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -465,7 +465,9 @@ public static String qualify(String prefix, String name) { } public static String[] qualify(String prefix, String[] names) { - if ( prefix == null ) return names; + if ( prefix == null ) { + return names; + } int len = names.length; String[] qualified = new String[len]; for ( int i = 0; i < len; i++ ) { @@ -473,6 +475,24 @@ public static String[] qualify(String prefix, String[] names) { } return qualified; } + + public static String[] qualifyIfNot(String prefix, String[] names) { + if ( prefix == null ) { + return names; + } + int len = names.length; + String[] qualified = new String[len]; + for ( int i = 0; i < len; i++ ) { + if ( names[i].indexOf( '.' ) < 0 ) { + qualified[i] = qualify( prefix, names[i] ); + } + else { + qualified[i] = names[i]; + } + } + return qualified; + } + public static int firstIndexOfChar(String sqlString, BitSet keys, int startindex) { for ( int i = startindex, size = sqlString.length(); i < size; i++ ) { if ( keys.get( sqlString.charAt( i ) ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java b/hibernate-core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java index 673741e8fb..9d39122c46 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java @@ -205,6 +205,16 @@ public final Loadable getPersister() { public final String getAlias() { return alias; } + + /** + * For entities, orderings added by, for example, Criteria#addOrder need to come before the associations' @OrderBy + * values. However, other sub-classes of JoinWalker (BasicCollectionJoinWalker, OneToManyJoinWalker, etc.) + * still need the other way around. So, override here instead. See HHH-7116. + */ + @Override + protected String orderBy(final List associations, final String orderBy) { + return mergeOrderings( orderBy, orderBy( associations ) ); + } public String toString() { return getClass().getName() + '(' + getPersister().getEntityName() + ')'; diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/id/IdTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/id/IdTest.java index b921de1616..55aff081f1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/id/IdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/id/IdTest.java @@ -23,6 +23,7 @@ */ package org.hibernate.test.annotations.id; +import org.hibernate.test.annotations.id.entities.Hotel; import org.junit.Test; import org.hibernate.Session; @@ -50,12 +51,51 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; /** * @author Emmanuel Bernard */ @SuppressWarnings("unchecked") public class IdTest extends BaseCoreFunctionalTestCase { + + @Test + public void testNoGenerator() throws Exception { + Session s = openSession(); + Transaction tx = s.beginTransaction(); + Hotel hotel = new Hotel(); + hotel.setId( 12l ); + hotel.setName("California"); + s.saveOrUpdate(hotel); + tx.commit(); + s.close(); + + s = openSession(); + tx = s.beginTransaction(); + hotel = (Hotel) s.get(Hotel.class, 12l); + assertNotNull(hotel); + assertEquals("California", hotel.getName()); + assertNull(s.get(Hotel.class, 13l)); + tx.commit(); + + s = openSession(); + tx = s.beginTransaction(); + //hotel is now detached + hotel.setName("Hotel du nord"); + s.saveOrUpdate(hotel); + tx.commit(); + s.close(); + + s = openSession(); + tx = s.beginTransaction(); + hotel = (Hotel) s.get(Hotel.class, 12l); + assertNotNull(hotel); + assertEquals("Hotel du nord", hotel.getName()); + s.delete(hotel); + tx.commit(); + s.close(); + } + @Test public void testGenericGenerator() throws Exception { Session s = openSession(); @@ -319,7 +359,7 @@ protected Class[] getAnnotatedClasses() { Department.class, Dog.class, Computer.class, Home.class, Phone.class, Tree.class, FirTree.class, Footballer.class, SoundSystem.class, Furniture.class, GoalKeeper.class, - BreakDance.class, Monkey.class}; + BreakDance.class, Monkey.class, Hotel.class }; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/id/entities/Hotel.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/id/entities/Hotel.java new file mode 100644 index 0000000000..ffb61941c6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/id/entities/Hotel.java @@ -0,0 +1,33 @@ +package org.hibernate.test.annotations.id.entities; + +import javax.persistence.Entity; +import javax.persistence.Id; + +/** + * Entity with assigned identity + * + * @author Emmanuel Bernard + */ +@Entity +public class Hotel { + @Id + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Attrset.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Attrset.java new file mode 100644 index 0000000000..d3f9344eb0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Attrset.java @@ -0,0 +1,37 @@ +package org.hibernate.test.annotations.query; + +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinTable; +import javax.persistence.OneToMany; + +@Entity +public class Attrset { + @Id + @GeneratedValue + private Long id; + + @OneToMany + @JoinTable(name = "ATTRSET_X_ATTRVALUE") + private Set attrvalues = new HashSet(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getAttrvalues() { + return attrvalues; + } + + public void setAttrvalues(Set attrvalues) { + this.attrvalues = attrvalues; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Attrvalue.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Attrvalue.java new file mode 100644 index 0000000000..2d0ef1a8ca --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Attrvalue.java @@ -0,0 +1,30 @@ +package org.hibernate.test.annotations.query; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class Attrvalue { + @Id + @GeneratedValue + private Long id; + + private String value; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Employee.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Employee.java new file mode 100644 index 0000000000..acc69e841b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Employee.java @@ -0,0 +1,44 @@ +package org.hibernate.test.annotations.query; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +@Entity +public class Employee { + @Id + @GeneratedValue + private Long id; + + @ManyToOne + private Employeegroup employeegroup; + + @ManyToOne + private Attrset attrset; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Employeegroup getEmployeegroup() { + return employeegroup; + } + + public void setEmployeegroup(Employeegroup employeegroup) { + this.employeegroup = employeegroup; + } + + public Attrset getAttrset() { + return attrset; + } + + public void setAttrset(Attrset attrset) { + this.attrset = attrset; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Employeegroup.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Employeegroup.java new file mode 100644 index 0000000000..020b5021dd --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/Employeegroup.java @@ -0,0 +1,49 @@ +package org.hibernate.test.annotations.query; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +@Entity +public class Employeegroup { + @Id + @GeneratedValue + private Long id; + + @OneToMany(cascade = CascadeType.ALL, mappedBy = "employeegroup") + private List employees = new ArrayList(); + + @ManyToOne + private Attrset attrset; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getEmployees() { + return employees; + } + + public void setEmployees(List employees) { + this.employees = employees; + } + + public Attrset getAttrset() { + return attrset; + } + + public void setAttrset(Attrset attrset) { + this.attrset = attrset; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java index 1d56ee6f81..e180ad9522 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java @@ -51,6 +51,7 @@ import org.hibernate.testing.FailureExpected; import org.hibernate.testing.FailureExpectedWithNewMetamodel; import org.hibernate.testing.SkipForDialect; +import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -472,7 +473,11 @@ protected Class[] getAnnotatedClasses() { Chaos.class, CasimirParticle.class, AllTables.class, - Dimensions.class + Dimensions.class, + Attrset.class, + Attrvalue.class, + Employee.class, + Employeegroup.class }; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/criteria/Bid.java b/hibernate-core/src/test/java/org/hibernate/test/criteria/Bid.java new file mode 100644 index 0000000000..334b80921e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/criteria/Bid.java @@ -0,0 +1,19 @@ +package org.hibernate.test.criteria; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import java.io.Serializable; + +/** + * @author tknowlton at iamhisfriend dot org + */ +@Entity +public class Bid implements Serializable { + @Id + float amount; + + @Id + @ManyToOne + Item item; +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/criteria/CriteriaOrderByTest.java b/hibernate-core/src/test/java/org/hibernate/test/criteria/CriteriaOrderByTest.java new file mode 100644 index 0000000000..6d69666c86 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/criteria/CriteriaOrderByTest.java @@ -0,0 +1,130 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.test.criteria; + +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.criterion.Projections; +import org.hibernate.sql.JoinType; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.transform.ResultTransformer; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +/** + * @author tknowlton at iamhisfriend dot org + */ +public class CriteriaOrderByTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Bid.class, Item.class }; + } + + @Test + @TestForIssue(jiraKey = "HHH-7116") + public void testCriteriaOrderBy() { + final Session s = openSession(); + final Transaction tx = s.beginTransaction(); + + Item item; + Bid bid; + + item = new Item(); + item.name = "ZZZZ"; + s.persist( item ); + + bid = new Bid(); + bid.amount = 444.44f; + bid.item = item; + s.persist( bid ); + + item = new Item(); + item.name = "AAAA"; + s.persist( item ); + + bid = new Bid(); + bid.amount = 222.22f; + bid.item = item; + s.persist( bid ); + + item = new Item(); + item.name = "MMMM"; + s.persist( item ); + + bid = new Bid(); + bid.amount = 999.99f; + bid.item = item; + s.persist( bid ); + + s.flush(); + + // For each item, ordered by name, show all bids made by bidders on this item. + // The joined collections item.bids and bidder.bids have orderings specified on the mappings. + // For some reason, the association mappings' ordering specifications are not honored if default (INNER) join + // type is used. + final Criteria criteria = s + .createCriteria( Item.class ) + .addOrder( org.hibernate.criterion.Order.asc( "this.name" ) ) + .createAlias( "this.bids", "i_bid", JoinType.LEFT_OUTER_JOIN ) + .setProjection( + Projections.projectionList().add( Projections.property( "this.name" ), "item_name" ) + .add( Projections.property( "i_bid.amount" ), "bid_amount" ) ) + .setResultTransformer( new ResultTransformer() { + boolean first = true; + Object[] previous; + + @Override + public Object transformTuple(Object[] tuple, String[] aliases) { + if ( first ) { + first = false; + previous = tuple; + } + else { + final String previousName = (String) previous[0]; + final String name = (String) tuple[0]; + + Assert.assertTrue( + "The resultset tuples should be ordered by item name, as specified on the Criteria", + previousName.compareTo( name ) < 1 ); + + previous = tuple; + } + + return tuple; + } + + @Override + public List transformList(List collection) { + return collection; + } + } ); + + criteria.list(); + + tx.rollback(); + s.close(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/criteria/Item.java b/hibernate-core/src/test/java/org/hibernate/test/criteria/Item.java new file mode 100644 index 0000000000..c1187fd012 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/criteria/Item.java @@ -0,0 +1,19 @@ +package org.hibernate.test.criteria; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +/** + * @author tknowlton at iamhisfriend dot org + */ +@Entity +public class Item implements Serializable { + @Id + String name; + + @OneToMany(mappedBy = "item", fetch = FetchType.EAGER) + @OrderBy("amount desc") + Set bids = new HashSet(); +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/DeleteWhereMemberOfTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/DeleteWhereMemberOfTest.java new file mode 100644 index 0000000000..a2c9aeabb7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/DeleteWhereMemberOfTest.java @@ -0,0 +1,66 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * 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. + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql; + +import org.hibernate.Session; + +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.test.annotations.query.Attrset; +import org.hibernate.test.annotations.query.Attrvalue; +import org.hibernate.test.annotations.query.Employee; +import org.hibernate.test.annotations.query.Employeegroup; + +/** + * @author Steve Ebersole + */ +public class DeleteWhereMemberOfTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Attrset.class, + Attrvalue.class, + Employee.class, + Employeegroup.class + }; + } + + @Test + @TestForIssue( jiraKey = "HHH-8318" ) +// @FailureExpected( jiraKey = "HHH-8318" ) + public void testDeleteMemberOf() { + final String qry = "delete Attrvalue aval where aval.id in ( " + + "select val2.id from Employee e, Employeegroup eg, Attrset aset, Attrvalue val2 " + + "where eg.id = e.employeegroup.id " + "and aset.id = e.attrset.id " + + "and val2 member of aset.attrvalues)"; + Session s = openSession(); + s.getTransaction().begin(); + s.createQuery( qry ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } +} diff --git a/libraries.gradle b/libraries.gradle index 794597b3e9..e273979ad4 100644 --- a/libraries.gradle +++ b/libraries.gradle @@ -51,7 +51,7 @@ ext { dom4j: 'dom4j:dom4j:1.6.1@jar', // Javassist - javassist: 'org.javassist:javassist:3.15.0-GA', + javassist: 'org.javassist:javassist:3.18.0-GA', // Infinsipan infinispan: "org.infinispan:infinispan-core:${infinispanVersion}", diff --git a/release/release.gradle b/release/release.gradle index 280d448965..6d58ce21a6 100644 --- a/release/release.gradle +++ b/release/release.gradle @@ -201,6 +201,17 @@ distributions { ) } + into( 'lib/osgi' ) { + from( + ( parent.project( 'hibernate-osgi' ).configurations.archives.allArtifacts.files.filter{ file -> !file.name.endsWith('-sources.jar') } + + parent.project( 'hibernate-osgi' ).configurations.runtime ) + - parent.project( 'hibernate-core' ).configurations.runtime + - parent.project( 'hibernate-core' ).configurations.archives.allArtifacts.files + - parent.project( 'hibernate-entitymanager' ).configurations.runtime + - parent.project( 'hibernate-entitymanager' ).configurations.archives.allArtifacts.files + ) + } + // todo : this closure is problematic as it does not write into the hibernate-release-$project.version directory // due to http://issues.gradle.org/browse/GRADLE-1450 [ 'hibernate-c3p0', 'hibernate-proxool', 'hibernate-ehcache', 'hibernate-infinispan' ].each { feature ->