diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java index 31d444325d..153c31affb 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java @@ -53,6 +53,7 @@ import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.InheritanceType; import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; @@ -1392,17 +1393,34 @@ public final class AnnotationBinder { if ( element.isAnnotationPresent( Id.class ) && element.isAnnotationPresent( Column.class ) ) { String columnName = element.getAnnotation( Column.class ).name(); for ( XProperty prop : declaringClass.getDeclaredProperties( AccessType.FIELD.getType() ) ) { - if ( prop.isAnnotationPresent( JoinColumn.class ) - && prop.getAnnotation( JoinColumn.class ).name().equals( columnName ) - && !prop.isAnnotationPresent( MapsId.class ) ) { - //create a PropertyData fpr the specJ property holding the mapping - PropertyData specJPropertyData = new PropertyInferredData( - declaringClass, //same dec - prop, // the actual @XToOne property - propertyAccessor, //TODO we should get the right accessor but the same as id would do - mappings.getReflectionManager() - ); - mappings.addPropertyAnnotatedWithMapsIdSpecj( entity, specJPropertyData, element.toString() ); + if ( !prop.isAnnotationPresent( MapsId.class ) ) { + /** + * The detection of a configured individual JoinColumn differs between Annotation + * and XML configuration processing. + */ + boolean isRequiredAnnotationPresent = false; + JoinColumns groupAnnotation = prop.getAnnotation(JoinColumns.class); + if ( ( prop.isAnnotationPresent( JoinColumn.class ) + && prop.getAnnotation( JoinColumn.class ).name().equals( columnName ) ) ) { + isRequiredAnnotationPresent = true; + } else if ( prop.isAnnotationPresent( JoinColumns.class ) ) { + for ( JoinColumn columnAnnotation : groupAnnotation.value() ) { + if ( columnName.equals(columnAnnotation.name() ) ) { + isRequiredAnnotationPresent = true; + break; + } + } + } + if ( isRequiredAnnotationPresent ) { + //create a PropertyData fpr the specJ property holding the mapping + PropertyData specJPropertyData = new PropertyInferredData( + declaringClass, //same dec + prop, // the actual @XToOne property + propertyAccessor, //TODO we should get the right accessor but the same as id would do + mappings.getReflectionManager() + ); + mappings.addPropertyAnnotatedWithMapsIdSpecj( entity, specJPropertyData, element.toString() ); + } } } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CompositeKeyDeleteTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CompositeKeyDeleteTest.java new file mode 100644 index 0000000000..52b2266560 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CompositeKeyDeleteTest.java @@ -0,0 +1,121 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates 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.annotations.derivedidentities.e1.b.specjmapid.lazy; + +import java.math.BigDecimal; +import java.util.List; + +import junit.framework.Assert; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.Item; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +public class CompositeKeyDeleteTest extends BaseCoreFunctionalTestCase { + + public String[] getMappings() { + return new String[] { "annotations/derivedidentities/e1/b/specjmapid/lazy/order_orm.xml" }; + } + + public CompositeKeyDeleteTest() { + System.setProperty( "hibernate.enable_specj_proprietary_syntax", "true" ); + } + /** + * This test checks to make sure the non null column is not updated with a + * null value when a CustomerInventory is removed. + */ + @Test + public void testRemove() { + Session s = openSession(); + Transaction tx = s.beginTransaction(); + + CustomerTwo c1 = new CustomerTwo( + "foo", "bar", "contact1", "100", new BigDecimal( 1000 ), new BigDecimal( 1000 ), new BigDecimal( 1000 ) + ); + + s.persist( c1 ); + s.flush(); + s.clear(); + + Item boat = new Item(); + boat.setId( "1" ); + boat.setName( "cruiser" ); + boat.setPrice( new BigDecimal( 500 ) ); + boat.setDescription( "a boat" ); + boat.setCategory( 42 ); + + s.persist( boat ); + + + Item house = new Item(); + house.setId( "2" ); + house.setName( "blada" ); + house.setPrice( new BigDecimal( 5000 ) ); + house.setDescription( "a house" ); + house.setCategory( 74 ); + + s.persist( house ); + s.flush(); + s.clear(); + + c1.addInventory( boat, 10, new BigDecimal( 5000 ) ); + + c1.addInventory( house, 100, new BigDecimal( 50000 ) ); + s.merge( c1 ); + Integer id = c1.getId(); + tx.commit(); + s.close(); + + s = openSession(); + tx = s.beginTransaction(); + + CustomerTwo c12 = ( CustomerTwo) s.createQuery( "select c from CustomerTwo c" ).uniqueResult(); + Assert.assertNotNull(c12); + List list = c12.getInventories(); + Assert.assertNotNull(list); + Assert.assertEquals(2, list.size()); + CustomerInventoryTwo ci = list.get(1); + list.remove(ci); + s.delete(ci); + s.flush(); + + tx.commit();//fail + s.close(); + + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + CustomerTwo.class, + CustomerInventoryTwo.class, + CustomerInventoryTwoPK.class, + Item.class + + }; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CustomerInventoryTwo.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CustomerInventoryTwo.java new file mode 100644 index 0000000000..ea34c711be --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CustomerInventoryTwo.java @@ -0,0 +1,147 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates 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.annotations.derivedidentities.e1.b.specjmapid.lazy; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Comparator; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.persistence.TableGenerator; +import javax.persistence.Version; + +import org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.Item; + +@NamedQueries({ @NamedQuery(name = "CustomerInventoryTwo.selectAll", query = "select a from CustomerInventoryTwo a") }) +@SuppressWarnings("serial") +@Entity +@Table(name = "O_CUSTINVENTORY") +@IdClass(CustomerInventoryTwoPK.class) +public class CustomerInventoryTwo implements Serializable, + Comparator { + + @Id + @TableGenerator(name = "inventory", table = "U_SEQUENCES", pkColumnName = "S_ID", valueColumnName = "S_NEXTNUM", pkColumnValue = "inventory", allocationSize = 1000) + @GeneratedValue(strategy = GenerationType.TABLE, generator = "inventory") + @Column(name = "CI_ID") + private Integer id; + + @Id + @Column(name = "CI_CUSTOMERID", insertable = false, updatable = false) + private int custId; + + @ManyToOne(cascade = CascadeType.MERGE) + @JoinColumn(name = "CI_CUSTOMERID", nullable = false) + private CustomerTwo customer; + + @ManyToOne(cascade = CascadeType.MERGE) + @JoinColumn(name = "CI_ITEMID") + private Item vehicle; + + @Column(name = "CI_VALUE") + private BigDecimal totalCost; + + @Column(name = "CI_QUANTITY") + private int quantity; + + @Version + @Column(name = "CI_VERSION") + private int version; + + protected CustomerInventoryTwo() { + } + + CustomerInventoryTwo(CustomerTwo customer, Item vehicle, int quantity, + BigDecimal totalValue) { + this.customer = customer; + this.vehicle = vehicle; + this.quantity = quantity; + this.totalCost = totalValue; + } + + public Item getVehicle() { + return vehicle; + } + + public BigDecimal getTotalCost() { + return totalCost; + } + + public int getQuantity() { + return quantity; + } + + public Integer getId() { + return id; + } + + public CustomerTwo getCustomer() { + return customer; + } + + public int getCustId() { + return custId; + } + + public int getVersion() { + return version; + } + + public int compare(CustomerInventoryTwo cdb1, CustomerInventoryTwo cdb2) { + return cdb1.id.compareTo(cdb2.id); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !(obj instanceof CustomerInventoryTwo)) { + return false; + } + if (this.id == ((CustomerInventoryTwo) obj).id) { + return true; + } + if (this.id != null && ((CustomerInventoryTwo) obj).id == null) { + return false; + } + if (this.id == null && ((CustomerInventoryTwo) obj).id != null) { + return false; + } + + return this.id.equals(((CustomerInventoryTwo) obj).id); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CustomerInventoryTwoPK.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CustomerInventoryTwoPK.java new file mode 100644 index 0000000000..236290a979 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CustomerInventoryTwoPK.java @@ -0,0 +1,67 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates 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.annotations.derivedidentities.e1.b.specjmapid.lazy; + +import java.io.Serializable; + +public class CustomerInventoryTwoPK implements Serializable { + + private Integer id; + private int custId; + + public CustomerInventoryTwoPK() { + } + + public CustomerInventoryTwoPK(Integer id, int custId) { + this.id = id; + this.custId = custId; + } + + public boolean equals(Object other) { + if ( other == this ) { + return true; + } + if ( other == null || getClass() != other.getClass() ) { + return false; + } + CustomerInventoryTwoPK cip = ( CustomerInventoryTwoPK ) other; + return ( custId == cip.custId && ( id == cip.id || + ( id != null && id.equals( cip.id ) ) ) ); + } + + public int hashCode() { + return ( id == null ? 0 : id.hashCode() ) ^ custId; + } + + public Integer getId() { + return id; + } + + public int getCustId() { + return custId; + } + + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CustomerTwo.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CustomerTwo.java new file mode 100644 index 0000000000..d548959eea --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/CustomerTwo.java @@ -0,0 +1,248 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates 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.annotations.derivedidentities.e1.b.specjmapid.lazy; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Version; + +import org.hibernate.test.annotations.derivedidentities.e1.b.specjmapid.Item; + +@SuppressWarnings("serial") +@NamedQueries({ + @NamedQuery(name = CustomerTwo.QUERY_ALL, + query = "select a from CustomerTwo a"), + @NamedQuery(name = CustomerTwo.QUERY_COUNT, + query = "select COUNT(a) from CustomerTwo a"), + @NamedQuery(name = CustomerTwo.QUERY_BY_CREDIT, + query = "SELECT c.id FROM CustomerTwo c WHERE c.creditLimit > :limit") +}) +@Entity +@Table(name = "O_CUSTOMER") +public class CustomerTwo implements Serializable{ + public static final String QUERY_ALL = "CustomerTwo.selectAll"; + public static final String QUERY_COUNT = "CustomerTwo.count"; + public static final String QUERY_BY_CREDIT = "CustomerTwo.selectByCreditLimit"; + + public static final String BAD_CREDIT = "BC"; + + @Id + @Column(name = "C_ID") + private int id; + + @Column(name = "C_FIRST") + private String firstName; + + @Column(name = "C_LAST") + private String lastName; + + @Column(name = "C_CONTACT") + private String contact; + + @Column(name = "C_CREDIT") + private String credit; + + @Column(name = "C_CREDIT_LIMIT") + private BigDecimal creditLimit; + + @Column(name = "C_SINCE") + @Temporal(TemporalType.DATE) + private Calendar since; + + @Column(name = "C_BALANCE") + private BigDecimal balance; + + @Column(name = "C_YTD_PAYMENT") + private BigDecimal ytdPayment; + + @OneToMany(targetEntity = CustomerInventoryTwo.class, + mappedBy = "customer", + cascade = CascadeType.ALL, + fetch = FetchType.EAGER) + private List customerInventories; + + + @Version + @Column(name = "C_VERSION") + private int version; + + protected CustomerTwo() { + } + + public CustomerTwo(String first, String last, + String contact, String credit, BigDecimal creditLimit, + BigDecimal balance, BigDecimal YtdPayment) { + + this.firstName = first; + this.lastName = last; + this.contact = contact; + this.since = Calendar.getInstance(); + this.credit = credit; + this.creditLimit = creditLimit; + this.balance = balance; + this.ytdPayment = YtdPayment; + } + + public Integer getId() { + return id; + } + + public void setId(Integer customerId) { + this.id = customerId; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getContact() { + return contact; + } + + public void setContact(String contact) { + this.contact = contact; + } + + public String getCredit() { + return credit; + } + + public void setCredit(String credit) { + this.credit = credit; + } + + public BigDecimal getCreditLimit() { + return creditLimit; + } + + public void setCreditLimit(BigDecimal creditLimit) { + this.creditLimit = creditLimit; + } + + public Calendar getSince() { + return since; + } + + public void setSince(Calendar since) { + this.since = since; + } + + public BigDecimal getBalance() { + return balance; + } + + public void setBalance(BigDecimal balance) { + this.balance = balance; + } + + public void changeBalance(BigDecimal change) { + setBalance( balance.add( change ).setScale( 2, BigDecimal.ROUND_DOWN ) ); + } + + public BigDecimal getYtdPayment() { + return ytdPayment; + } + + public void setYtdPayment(BigDecimal ytdPayment) { + this.ytdPayment = ytdPayment; + } + + + public List getInventories() { + if ( this.customerInventories == null ) { + this.customerInventories = new ArrayList(); + } + return this.customerInventories; + } + + public CustomerInventoryTwo addInventory(Item item, int quantity, + BigDecimal totalValue) { + + CustomerInventoryTwo inventory = new CustomerInventoryTwo( + this, item, + quantity, totalValue + ); + getInventories().add( inventory ); + return inventory; + } + + public int getVersion() { + return version; + } + + public boolean hasSufficientCredit(BigDecimal amount) { + return !BAD_CREDIT.equals( getCredit() ) + && creditLimit != null + && creditLimit.compareTo( amount ) >= 0; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + return id == ( ( CustomerTwo ) o ).id; + } + + @Override + public int hashCode() { + return new Integer( id ).hashCode(); + } + + @Override + public String toString() { + return this.getFirstName() + " " + this.getLastName(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/hbm_order.xml b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/hbm_order.xml new file mode 100644 index 0000000000..511abfbb59 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/hbm_order.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/order_orm.xml b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/order_orm.xml new file mode 100644 index 0000000000..259e3cba10 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/specjmapid/lazy/order_orm.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +