diff --git a/hibernate-types-4/pom.xml b/hibernate-types-4/pom.xml index e9e706a2a..838459696 100644 --- a/hibernate-types-4/pom.xml +++ b/hibernate-types-4/pom.xml @@ -47,6 +47,15 @@ true + + org.javamoney + moneta + ${moneta.version} + pom + provided + true + + org.hibernate hibernate-ehcache @@ -70,6 +79,7 @@ 2.7.9.6 12.0 + 1.4.2 diff --git a/hibernate-types-43/pom.xml b/hibernate-types-43/pom.xml index 0eb0e2236..70e488784 100644 --- a/hibernate-types-43/pom.xml +++ b/hibernate-types-43/pom.xml @@ -47,6 +47,15 @@ true + + org.javamoney + moneta + ${moneta.version} + pom + provided + true + + org.hibernate hibernate-ehcache @@ -70,6 +79,7 @@ 2.7.9.6 12.0 + 1.4.2 diff --git a/hibernate-types-5/pom.xml b/hibernate-types-5/pom.xml index 6843db8a2..5c329f4b9 100644 --- a/hibernate-types-5/pom.xml +++ b/hibernate-types-5/pom.xml @@ -47,6 +47,15 @@ true + + org.javamoney + moneta + ${moneta.version} + pom + provided + true + + org.hibernate hibernate-ehcache @@ -70,6 +79,7 @@ 2.7.9.6 12.0 + 1.4.2 diff --git a/hibernate-types-52/pom.xml b/hibernate-types-52/pom.xml index d1757ead2..e603650db 100644 --- a/hibernate-types-52/pom.xml +++ b/hibernate-types-52/pom.xml @@ -46,6 +46,15 @@ true + + org.javamoney + moneta + ${moneta.version} + pom + provided + true + + org.hibernate hibernate-ehcache @@ -74,6 +83,7 @@ 2.12.6.1 2.12.6 31.1-jre + 1.4.2 diff --git a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/money/MonetaryAmountType.java b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/money/MonetaryAmountType.java new file mode 100644 index 000000000..096afb1c5 --- /dev/null +++ b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/money/MonetaryAmountType.java @@ -0,0 +1,128 @@ +package com.vladmihalcea.hibernate.type.money; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.type.BigDecimalType; +import org.hibernate.type.StringType; +import org.hibernate.type.Type; +import org.hibernate.usertype.CompositeUserType; +import org.javamoney.moneta.Money; + +import javax.money.MonetaryAmount; +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Objects; + +import static java.sql.Types.DECIMAL; +import static java.sql.Types.VARCHAR; + +/** + * Maps a {@link MonetaryAmount} object type to composite columns (with amount and with currency). + * + * @author Piotr Olaszewski + */ +public class MonetaryAmountType implements CompositeUserType { + + @Override + public String[] getPropertyNames() { + return new String[]{"amount", "currency"}; + } + + @Override + public Type[] getPropertyTypes() { + return new Type[]{BigDecimalType.INSTANCE, StringType.INSTANCE}; + } + + @Override + public Object getPropertyValue(Object component, int property) throws HibernateException { + MonetaryAmount monetaryAmount = (MonetaryAmount) component; + if (property == 0) { + return monetaryAmount.getNumber().numberValue(BigDecimal.class); + } else if (property == 1) { + return monetaryAmount.getCurrency(); + } + + throw new IllegalArgumentException("Invalid property index " + property + " for class " + component.getClass()); + } + + @Override + public void setPropertyValue(Object component, int property, Object value) throws HibernateException { + throw new HibernateException("Call setPropertyValue on immutable type " + component.getClass()); + } + + @Override + public Class returnedClass() { + return MonetaryAmount.class; + } + + @Override + public boolean equals(Object x, Object y) throws HibernateException { + return Objects.equals(x, y); + } + + @Override + public int hashCode(Object x) throws HibernateException { + return x.hashCode(); + } + + @Override + public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { + if (rs.wasNull()) { + return null; + } + + String amountColumnName = names[0]; + String currencyColumnName = names[1]; + + BigDecimal amount = rs.getBigDecimal(amountColumnName); + String currency = rs.getString(currencyColumnName); + + return Money.of(amount, currency); + } + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int amountColumnIndex, SharedSessionContractImplementor session) throws HibernateException, SQLException { + int currencyColumnIndex = amountColumnIndex + 1; + + if (value == null) { + st.setNull(amountColumnIndex, DECIMAL); + st.setNull(currencyColumnIndex, VARCHAR); + } else { + MonetaryAmount monetaryAmount = (MonetaryAmount) value; + + BigDecimal amount = monetaryAmount.getNumber().numberValue(BigDecimal.class); + String currency = monetaryAmount.getCurrency().getCurrencyCode(); + + st.setBigDecimal(amountColumnIndex, amount); + st.setString(currencyColumnIndex, currency); + } + } + + @Override + public Object deepCopy(Object value) throws HibernateException { + return value; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(Object value, SharedSessionContractImplementor session) throws HibernateException { + return (Serializable) value; + } + + @Override + public Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException { + return cached; + } + + @Override + public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner) throws HibernateException { + return original; + } +} diff --git a/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/money/MySQLMonetaryAmountTypeTest.java b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/money/MySQLMonetaryAmountTypeTest.java new file mode 100644 index 000000000..34ce9c1ce --- /dev/null +++ b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/money/MySQLMonetaryAmountTypeTest.java @@ -0,0 +1,98 @@ +package com.vladmihalcea.hibernate.type.money; + +import com.vladmihalcea.hibernate.util.AbstractMySQLIntegrationTest; +import org.hibernate.annotations.Columns; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.javamoney.moneta.Money; +import org.junit.Test; + +import javax.money.MonetaryAmount; +import javax.persistence.*; +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; + +/** + * @author Piotr Olaszewski + */ +public class MySQLMonetaryAmountTypeTest extends AbstractMySQLIntegrationTest { + @Override + protected Class[] entities() { + return new Class[]{ + Salary.class + }; + } + + @Test + public void testPersistAndReadMoney() { + Salary _salary = doInJPA(entityManager -> { + Salary salary = new Salary(); + salary.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + + entityManager.persist(salary); + + return salary; + }); + + doInJPA(entityManager -> { + Salary salary = entityManager.find(Salary.class, _salary.getId()); + + assertEquals(salary.getSalary(), Money.of(new BigDecimal("10.23"), "USD")); + }); + } + + @Test + public void testSearchByMoney() { + doInJPA(entityManager -> { + Salary salary1 = new Salary(); + salary1.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + entityManager.persist(salary1); + + Salary salary2 = new Salary(); + salary2.setSalary(Money.of(new BigDecimal("20.23"), "EUR")); + entityManager.persist(salary2); + }); + + doInJPA(entityManager -> { + Money money = Money.of(new BigDecimal("10.23"), "USD"); + Salary salary = entityManager.createQuery("select s from Salary s where salary = :salary", Salary.class) + .setParameter("salary", money) + .getSingleResult(); + + assertEquals(Long.valueOf(1), salary.getId()); + }); + } + + @Entity(name = "Salary") + @Table(name = "salary") + @TypeDef(name = "monetary-amount-currency", typeClass = MonetaryAmountType.class, defaultForType = MonetaryAmount.class) + public static class Salary { + @Id + @GeneratedValue + private Long id; + + @Columns(columns = { + @Column(name = "salary_amount"), + @Column(name = "salary_currency") + }) + @Type(type = "monetary-amount-currency") + private MonetaryAmount salary; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MonetaryAmount getSalary() { + return salary; + } + + public void setSalary(MonetaryAmount salary) { + this.salary = salary; + } + } +} diff --git a/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/money/PostgreSQLMonetaryAmountTypeTest.java b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/money/PostgreSQLMonetaryAmountTypeTest.java new file mode 100644 index 000000000..c537e68b5 --- /dev/null +++ b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/money/PostgreSQLMonetaryAmountTypeTest.java @@ -0,0 +1,98 @@ +package com.vladmihalcea.hibernate.type.money; + +import com.vladmihalcea.hibernate.util.AbstractPostgreSQLIntegrationTest; +import org.hibernate.annotations.Columns; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.javamoney.moneta.Money; +import org.junit.Test; + +import javax.money.MonetaryAmount; +import javax.persistence.*; +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; + +/** + * @author Piotr Olaszewski + */ +public class PostgreSQLMonetaryAmountTypeTest extends AbstractPostgreSQLIntegrationTest { + @Override + protected Class[] entities() { + return new Class[]{ + Salary.class + }; + } + + @Test + public void testPersistAndReadMoney() { + Salary _salary = doInJPA(entityManager -> { + Salary salary = new Salary(); + salary.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + + entityManager.persist(salary); + + return salary; + }); + + doInJPA(entityManager -> { + Salary salary = entityManager.find(Salary.class, _salary.getId()); + + assertEquals(salary.getSalary(), Money.of(new BigDecimal("10.23"), "USD")); + }); + } + + @Test + public void testSearchByMoney() { + doInJPA(entityManager -> { + Salary salary1 = new Salary(); + salary1.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + entityManager.persist(salary1); + + Salary salary2 = new Salary(); + salary2.setSalary(Money.of(new BigDecimal("20.23"), "EUR")); + entityManager.persist(salary2); + }); + + doInJPA(entityManager -> { + Money money = Money.of(new BigDecimal("10.23"), "USD"); + Salary salary = entityManager.createQuery("select s from Salary s where salary = :salary", Salary.class) + .setParameter("salary", money) + .getSingleResult(); + + assertEquals(Long.valueOf(1), salary.getId()); + }); + } + + @Entity(name = "Salary") + @Table(name = "salary") + @TypeDef(name = "monetary-amount-currency", typeClass = MonetaryAmountType.class, defaultForType = MonetaryAmount.class) + public static class Salary { + @Id + @GeneratedValue + private Long id; + + @Columns(columns = { + @Column(name = "salary_amount"), + @Column(name = "salary_currency") + }) + @Type(type = "monetary-amount-currency") + private MonetaryAmount salary; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MonetaryAmount getSalary() { + return salary; + } + + public void setSalary(MonetaryAmount salary) { + this.salary = salary; + } + } +} diff --git a/hibernate-types-55/pom.xml b/hibernate-types-55/pom.xml index e74fc5551..8fba02afb 100644 --- a/hibernate-types-55/pom.xml +++ b/hibernate-types-55/pom.xml @@ -46,6 +46,15 @@ true + + org.javamoney + moneta + ${moneta.version} + pom + provided + true + + org.hibernate hibernate-ehcache @@ -73,6 +82,7 @@ 2.12.6.1 2.12.6 31.1-jre + 1.4.2 diff --git a/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/money/MonetaryAmountType.java b/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/money/MonetaryAmountType.java new file mode 100644 index 000000000..096afb1c5 --- /dev/null +++ b/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/money/MonetaryAmountType.java @@ -0,0 +1,128 @@ +package com.vladmihalcea.hibernate.type.money; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.type.BigDecimalType; +import org.hibernate.type.StringType; +import org.hibernate.type.Type; +import org.hibernate.usertype.CompositeUserType; +import org.javamoney.moneta.Money; + +import javax.money.MonetaryAmount; +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Objects; + +import static java.sql.Types.DECIMAL; +import static java.sql.Types.VARCHAR; + +/** + * Maps a {@link MonetaryAmount} object type to composite columns (with amount and with currency). + * + * @author Piotr Olaszewski + */ +public class MonetaryAmountType implements CompositeUserType { + + @Override + public String[] getPropertyNames() { + return new String[]{"amount", "currency"}; + } + + @Override + public Type[] getPropertyTypes() { + return new Type[]{BigDecimalType.INSTANCE, StringType.INSTANCE}; + } + + @Override + public Object getPropertyValue(Object component, int property) throws HibernateException { + MonetaryAmount monetaryAmount = (MonetaryAmount) component; + if (property == 0) { + return monetaryAmount.getNumber().numberValue(BigDecimal.class); + } else if (property == 1) { + return monetaryAmount.getCurrency(); + } + + throw new IllegalArgumentException("Invalid property index " + property + " for class " + component.getClass()); + } + + @Override + public void setPropertyValue(Object component, int property, Object value) throws HibernateException { + throw new HibernateException("Call setPropertyValue on immutable type " + component.getClass()); + } + + @Override + public Class returnedClass() { + return MonetaryAmount.class; + } + + @Override + public boolean equals(Object x, Object y) throws HibernateException { + return Objects.equals(x, y); + } + + @Override + public int hashCode(Object x) throws HibernateException { + return x.hashCode(); + } + + @Override + public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { + if (rs.wasNull()) { + return null; + } + + String amountColumnName = names[0]; + String currencyColumnName = names[1]; + + BigDecimal amount = rs.getBigDecimal(amountColumnName); + String currency = rs.getString(currencyColumnName); + + return Money.of(amount, currency); + } + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int amountColumnIndex, SharedSessionContractImplementor session) throws HibernateException, SQLException { + int currencyColumnIndex = amountColumnIndex + 1; + + if (value == null) { + st.setNull(amountColumnIndex, DECIMAL); + st.setNull(currencyColumnIndex, VARCHAR); + } else { + MonetaryAmount monetaryAmount = (MonetaryAmount) value; + + BigDecimal amount = monetaryAmount.getNumber().numberValue(BigDecimal.class); + String currency = monetaryAmount.getCurrency().getCurrencyCode(); + + st.setBigDecimal(amountColumnIndex, amount); + st.setString(currencyColumnIndex, currency); + } + } + + @Override + public Object deepCopy(Object value) throws HibernateException { + return value; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(Object value, SharedSessionContractImplementor session) throws HibernateException { + return (Serializable) value; + } + + @Override + public Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException { + return cached; + } + + @Override + public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner) throws HibernateException { + return original; + } +} diff --git a/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/money/MySQLMonetaryAmountTypeTest.java b/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/money/MySQLMonetaryAmountTypeTest.java new file mode 100644 index 000000000..34ce9c1ce --- /dev/null +++ b/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/money/MySQLMonetaryAmountTypeTest.java @@ -0,0 +1,98 @@ +package com.vladmihalcea.hibernate.type.money; + +import com.vladmihalcea.hibernate.util.AbstractMySQLIntegrationTest; +import org.hibernate.annotations.Columns; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.javamoney.moneta.Money; +import org.junit.Test; + +import javax.money.MonetaryAmount; +import javax.persistence.*; +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; + +/** + * @author Piotr Olaszewski + */ +public class MySQLMonetaryAmountTypeTest extends AbstractMySQLIntegrationTest { + @Override + protected Class[] entities() { + return new Class[]{ + Salary.class + }; + } + + @Test + public void testPersistAndReadMoney() { + Salary _salary = doInJPA(entityManager -> { + Salary salary = new Salary(); + salary.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + + entityManager.persist(salary); + + return salary; + }); + + doInJPA(entityManager -> { + Salary salary = entityManager.find(Salary.class, _salary.getId()); + + assertEquals(salary.getSalary(), Money.of(new BigDecimal("10.23"), "USD")); + }); + } + + @Test + public void testSearchByMoney() { + doInJPA(entityManager -> { + Salary salary1 = new Salary(); + salary1.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + entityManager.persist(salary1); + + Salary salary2 = new Salary(); + salary2.setSalary(Money.of(new BigDecimal("20.23"), "EUR")); + entityManager.persist(salary2); + }); + + doInJPA(entityManager -> { + Money money = Money.of(new BigDecimal("10.23"), "USD"); + Salary salary = entityManager.createQuery("select s from Salary s where salary = :salary", Salary.class) + .setParameter("salary", money) + .getSingleResult(); + + assertEquals(Long.valueOf(1), salary.getId()); + }); + } + + @Entity(name = "Salary") + @Table(name = "salary") + @TypeDef(name = "monetary-amount-currency", typeClass = MonetaryAmountType.class, defaultForType = MonetaryAmount.class) + public static class Salary { + @Id + @GeneratedValue + private Long id; + + @Columns(columns = { + @Column(name = "salary_amount"), + @Column(name = "salary_currency") + }) + @Type(type = "monetary-amount-currency") + private MonetaryAmount salary; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MonetaryAmount getSalary() { + return salary; + } + + public void setSalary(MonetaryAmount salary) { + this.salary = salary; + } + } +} diff --git a/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/money/PostgreSQLMonetaryAmountTypeTest.java b/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/money/PostgreSQLMonetaryAmountTypeTest.java new file mode 100644 index 000000000..c537e68b5 --- /dev/null +++ b/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/money/PostgreSQLMonetaryAmountTypeTest.java @@ -0,0 +1,98 @@ +package com.vladmihalcea.hibernate.type.money; + +import com.vladmihalcea.hibernate.util.AbstractPostgreSQLIntegrationTest; +import org.hibernate.annotations.Columns; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.javamoney.moneta.Money; +import org.junit.Test; + +import javax.money.MonetaryAmount; +import javax.persistence.*; +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; + +/** + * @author Piotr Olaszewski + */ +public class PostgreSQLMonetaryAmountTypeTest extends AbstractPostgreSQLIntegrationTest { + @Override + protected Class[] entities() { + return new Class[]{ + Salary.class + }; + } + + @Test + public void testPersistAndReadMoney() { + Salary _salary = doInJPA(entityManager -> { + Salary salary = new Salary(); + salary.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + + entityManager.persist(salary); + + return salary; + }); + + doInJPA(entityManager -> { + Salary salary = entityManager.find(Salary.class, _salary.getId()); + + assertEquals(salary.getSalary(), Money.of(new BigDecimal("10.23"), "USD")); + }); + } + + @Test + public void testSearchByMoney() { + doInJPA(entityManager -> { + Salary salary1 = new Salary(); + salary1.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + entityManager.persist(salary1); + + Salary salary2 = new Salary(); + salary2.setSalary(Money.of(new BigDecimal("20.23"), "EUR")); + entityManager.persist(salary2); + }); + + doInJPA(entityManager -> { + Money money = Money.of(new BigDecimal("10.23"), "USD"); + Salary salary = entityManager.createQuery("select s from Salary s where salary = :salary", Salary.class) + .setParameter("salary", money) + .getSingleResult(); + + assertEquals(Long.valueOf(1), salary.getId()); + }); + } + + @Entity(name = "Salary") + @Table(name = "salary") + @TypeDef(name = "monetary-amount-currency", typeClass = MonetaryAmountType.class, defaultForType = MonetaryAmount.class) + public static class Salary { + @Id + @GeneratedValue + private Long id; + + @Columns(columns = { + @Column(name = "salary_amount"), + @Column(name = "salary_currency") + }) + @Type(type = "monetary-amount-currency") + private MonetaryAmount salary; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MonetaryAmount getSalary() { + return salary; + } + + public void setSalary(MonetaryAmount salary) { + this.salary = salary; + } + } +} diff --git a/hibernate-types-60/pom.xml b/hibernate-types-60/pom.xml index 7d649907f..9d8290880 100644 --- a/hibernate-types-60/pom.xml +++ b/hibernate-types-60/pom.xml @@ -46,6 +46,15 @@ true + + org.javamoney + moneta + ${moneta.version} + pom + provided + true + + org.hibernate hibernate-jcache @@ -88,6 +97,7 @@ 2.12.6.1 2.12.6 31.1-jre + 1.4.2 3.10.0 diff --git a/hibernate-types-60/src/main/java/com/vladmihalcea/hibernate/type/money/MonetaryAmountType.java b/hibernate-types-60/src/main/java/com/vladmihalcea/hibernate/type/money/MonetaryAmountType.java new file mode 100644 index 000000000..0e1ae6838 --- /dev/null +++ b/hibernate-types-60/src/main/java/com/vladmihalcea/hibernate/type/money/MonetaryAmountType.java @@ -0,0 +1,92 @@ +package com.vladmihalcea.hibernate.type.money; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.spi.ValueAccess; +import org.hibernate.usertype.CompositeUserType; +import org.javamoney.moneta.Money; + +import javax.money.MonetaryAmount; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Objects; + +/** + * Maps a {@link MonetaryAmount} object type to composite columns (with amount and with currency). + * + * @author Piotr Olaszewski + */ +public class MonetaryAmountType implements CompositeUserType { + public static class MonetaryAmountMapper { + BigDecimal amount; + String currency; + } + + public MonetaryAmountType() { + } + + @Override + public Object getPropertyValue(MonetaryAmount component, int property) throws HibernateException { + // alphabetical (amount, currency) + switch (property) { + case 0: + return component.getNumber().numberValue(BigDecimal.class); + case 1: + return component.getCurrency().getCurrencyCode(); + } + return null; + } + + @Override + public MonetaryAmount instantiate(ValueAccess values, SessionFactoryImplementor sessionFactory) { + // alphabetical (amount, currency) + BigDecimal amount = values.getValue(0, BigDecimal.class); + String currency = values.getValue(1, String.class); + return Money.of(amount, currency); + } + + @Override + public Class embeddable() { + return MonetaryAmountMapper.class; + } + + @Override + public Class returnedClass() { + return MonetaryAmount.class; + } + + @Override + public boolean equals(MonetaryAmount x, MonetaryAmount y) { + return Objects.equals(x, y); + } + + @Override + public int hashCode(MonetaryAmount x) { + return x.hashCode(); + } + + @Override + public MonetaryAmount deepCopy(MonetaryAmount value) { + return value; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(MonetaryAmount value) { + return (Serializable) value; + } + + @Override + public MonetaryAmount assemble(Serializable cached, Object owner) { + return (MonetaryAmount) cached; + } + + @Override + public MonetaryAmount replace(MonetaryAmount detached, MonetaryAmount managed, Object owner) { + return detached; + } +} diff --git a/hibernate-types-60/src/test/java/com/vladmihalcea/hibernate/type/money/MySQLMonetaryAmountTypeTest.java b/hibernate-types-60/src/test/java/com/vladmihalcea/hibernate/type/money/MySQLMonetaryAmountTypeTest.java new file mode 100644 index 000000000..7da4bd3a0 --- /dev/null +++ b/hibernate-types-60/src/test/java/com/vladmihalcea/hibernate/type/money/MySQLMonetaryAmountTypeTest.java @@ -0,0 +1,93 @@ +package com.vladmihalcea.hibernate.type.money; + +import com.vladmihalcea.hibernate.util.AbstractMySQLIntegrationTest; +import jakarta.persistence.*; +import org.hibernate.annotations.CompositeType; +import org.javamoney.moneta.Money; +import org.junit.Test; + +import javax.money.MonetaryAmount; +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; + +/** + * @author Piotr Olaszewski + */ +public class MySQLMonetaryAmountTypeTest extends AbstractMySQLIntegrationTest { + @Override + protected Class[] entities() { + return new Class[]{ + Salary.class + }; + } + + @Test + public void testPersistAndReadMoney() { + Salary _salary = doInJPA(entityManager -> { + Salary salary = new Salary(); + salary.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + + entityManager.persist(salary); + + return salary; + }); + + doInJPA(entityManager -> { + Salary salary = entityManager.find(Salary.class, _salary.getId()); + + assertEquals(salary.getSalary(), Money.of(new BigDecimal("10.23"), "USD")); + }); + } + + @Test + public void testSearchByMoney() { + doInJPA(entityManager -> { + Salary salary1 = new Salary(); + salary1.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + entityManager.persist(salary1); + + Salary salary2 = new Salary(); + salary2.setSalary(Money.of(new BigDecimal("20.23"), "EUR")); + entityManager.persist(salary2); + }); + + doInJPA(entityManager -> { + Money money = Money.of(new BigDecimal("10.23"), "USD"); + Salary salary = entityManager.createQuery("select s from Salary s where salary = :salary", Salary.class) + .setParameter("salary", money) + .getSingleResult(); + + assertEquals(Long.valueOf(1), salary.getId()); + }); + } + + @Entity(name = "Salary") + @Table(name = "salary") + public static class Salary { + @Id + @GeneratedValue + private Long id; + + @AttributeOverride(name = "amount", column = @Column(name = "salary_amount")) + @AttributeOverride(name = "currency", column = @Column(name = "salary_currency")) + @CompositeType(MonetaryAmountType.class) + private MonetaryAmount salary; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MonetaryAmount getSalary() { + return salary; + } + + public void setSalary(MonetaryAmount salary) { + this.salary = salary; + } + } +} diff --git a/hibernate-types-60/src/test/java/com/vladmihalcea/hibernate/type/money/PostgreSQLMonetaryAmountTypeTest.java b/hibernate-types-60/src/test/java/com/vladmihalcea/hibernate/type/money/PostgreSQLMonetaryAmountTypeTest.java new file mode 100644 index 000000000..7778c7794 --- /dev/null +++ b/hibernate-types-60/src/test/java/com/vladmihalcea/hibernate/type/money/PostgreSQLMonetaryAmountTypeTest.java @@ -0,0 +1,93 @@ +package com.vladmihalcea.hibernate.type.money; + +import com.vladmihalcea.hibernate.util.AbstractPostgreSQLIntegrationTest; +import jakarta.persistence.*; +import org.hibernate.annotations.CompositeType; +import org.javamoney.moneta.Money; +import org.junit.Test; + +import javax.money.MonetaryAmount; +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; + +/** + * @author Piotr Olaszewski + */ +public class PostgreSQLMonetaryAmountTypeTest extends AbstractPostgreSQLIntegrationTest { + @Override + protected Class[] entities() { + return new Class[]{ + Salary.class + }; + } + + @Test + public void testPersistAndReadMoney() { + Salary _salary = doInJPA(entityManager -> { + Salary salary = new Salary(); + salary.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + + entityManager.persist(salary); + + return salary; + }); + + doInJPA(entityManager -> { + Salary salary = entityManager.find(Salary.class, _salary.getId()); + + assertEquals(salary.getSalary(), Money.of(new BigDecimal("10.23"), "USD")); + }); + } + + @Test + public void testSearchByMoney() { + doInJPA(entityManager -> { + Salary salary1 = new Salary(); + salary1.setSalary(Money.of(new BigDecimal("10.23"), "USD")); + entityManager.persist(salary1); + + Salary salary2 = new Salary(); + salary2.setSalary(Money.of(new BigDecimal("20.23"), "EUR")); + entityManager.persist(salary2); + }); + + doInJPA(entityManager -> { + Money money = Money.of(new BigDecimal("10.23"), "USD"); + Salary salary = entityManager.createQuery("select s from Salary s where salary = :salary", Salary.class) + .setParameter("salary", money) + .getSingleResult(); + + assertEquals(Long.valueOf(1), salary.getId()); + }); + } + + @Entity(name = "Salary") + @Table(name = "salary") + public static class Salary { + @Id + @GeneratedValue + private Long id; + + @AttributeOverride(name = "amount", column = @Column(name = "salary_amount")) + @AttributeOverride(name = "currency", column = @Column(name = "salary_currency")) + @CompositeType(MonetaryAmountType.class) + private MonetaryAmount salary; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MonetaryAmount getSalary() { + return salary; + } + + public void setSalary(MonetaryAmount salary) { + this.salary = salary; + } + } +}