Skip to content

Commit

Permalink
Add support for querying by the embedded MonetaryAmount attributes #497
Browse files Browse the repository at this point in the history
  • Loading branch information
nstdio authored and vladmihalcea committed Oct 17, 2022
1 parent 160407b commit 7b02229
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 36 deletions.
@@ -1,27 +1,26 @@
package com.vladmihalcea.hibernate.type;

import com.vladmihalcea.hibernate.type.util.Configuration;
import org.hibernate.EntityMode;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.*;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.type.BasicType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.ForeignKeyDirection;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.IncomparableComparator;
import org.hibernate.usertype.CompositeUserType;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;

/**
Expand All @@ -33,21 +32,22 @@
*
* @author Vlad Mihalcea
*/
public abstract class ImmutableCompositeType<T> implements CompositeUserType, BasicType {
public abstract class ImmutableCompositeType<T> implements CompositeUserType, CompositeType, BasicType {

private final Configuration configuration;

private final Class<T> clazz;

private final List<Method> clazzMethods;

/**
* Initialization constructor taking the {@link Class}
* and using the default {@link Configuration} object.
*
* @param clazz the entity attribute {@link Class} type to be handled
*/
protected ImmutableCompositeType(Class<T> clazz) {
this.clazz = clazz;
this.configuration = Configuration.INSTANCE;
this(clazz, Configuration.INSTANCE);
}

/**
Expand All @@ -59,6 +59,7 @@ protected ImmutableCompositeType(Class<T> clazz) {
protected ImmutableCompositeType(Class<T> clazz, Configuration configuration) {
this.clazz = clazz;
this.configuration = configuration;
this.clazzMethods = Collections.unmodifiableList(Arrays.asList(clazz.getMethods()));
}

/**
Expand Down Expand Up @@ -173,7 +174,7 @@ public boolean isAnyType() {

@Override
public boolean isComponentType() {
return false;
return true;
}

@Override
Expand Down Expand Up @@ -333,4 +334,59 @@ public String[] getRegistrationKeys() {
getName()
};
}

@Override
public Type[] getSubtypes() {
return getPropertyTypes();
}

@Override
public boolean[] getPropertyNullability() {
return new boolean[]{false, false};
}

@Override
public Object[] getPropertyValues(Object component, SharedSessionContractImplementor session) throws HibernateException {
return new Object[]{getPropertyValue(component, 0), getPropertyValue(component, 1)};
}

@Override
public Object[] getPropertyValues(Object component, EntityMode entityMode) throws HibernateException {
return new Object[]{getPropertyValue(component, 0), getPropertyValue(component, 1)};
}

@Override
public Object getPropertyValue(Object component, int index, SharedSessionContractImplementor session) throws HibernateException {
return getPropertyValue(component, index);
}

@Override
public void setPropertyValues(Object component, Object[] values, EntityMode entityMode) throws HibernateException {
throw new HibernateException("Calling setPropertyValues is illegal on on " + clazz.getName() + " because it's an immutable object!");
}

@Override
public CascadeStyle getCascadeStyle(int index) {
return CascadeStyles.NONE;
}

@Override
public FetchMode getFetchMode(int index) {
return FetchMode.DEFAULT;
}

@Override
public boolean isMethodOf(Method method) {
return clazzMethods.contains(method);
}

@Override
public boolean isEmbedded() {
return false;
}

@Override
public boolean hasNotNullProperty() {
return true;
}
}
Expand Up @@ -27,6 +27,7 @@
* on <a href="https://vladmihalcea.com/">vladmihalcea.com</a>.
*
* @author Piotr Olaszewski
* @author Edgar Asatryan
*/
public class MonetaryAmountType extends ImmutableCompositeType<MonetaryAmount> {

Expand Down Expand Up @@ -99,4 +100,13 @@ protected void set(PreparedStatement st, MonetaryAmount value, int amountColumnI
st.setString(currencyColumnIndex, currency);
}
}

@Override
public int getPropertyIndex(String propertyName) {
switch (propertyName) {
case "amount": return 0;
case "property": return 1;
default: throw new IllegalArgumentException("Unknown property name: " + propertyName);
}
}
}
Expand Up @@ -10,6 +10,7 @@
import javax.money.MonetaryAmount;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
Expand Down Expand Up @@ -61,7 +62,7 @@ public void testSearchByMoney() {
.setParameter("salary", money)
.getSingleResult();

assertEquals(Long.valueOf(1), salary.getId());
assertEquals(1L, salary.getId());
});
}

Expand Down Expand Up @@ -90,13 +91,45 @@ public void testReturnNullMoney() {
});
}

@Test
public void testSearchByComponents() {
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 -> {
BigDecimal amount = BigDecimal.TEN;
List<Salary> salaries = entityManager.createQuery("select s from Salary s where s.salary.amount >= :amount", Salary.class)
.setParameter("amount", amount)
.getResultList();

assertEquals(1L, salaries.get(0).getId());
assertEquals(2L, salaries.get(1).getId());
});

doInJPA(entityManager -> {
String currency = "USD";
Salary salary = entityManager.createQuery("select s from Salary s where s.salary.currency = :currency", Salary.class)
.setParameter("currency", currency)
.getSingleResult();

assertEquals(1L, 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;
private long id;

private String other;

Expand All @@ -107,11 +140,11 @@ public static class Salary {
@Type(type = "monetary-amount-currency")
private MonetaryAmount salary;

public Long getId() {
public long getId() {
return id;
}

public void setId(Long id) {
public void setId(long id) {
this.id = id;
}

Expand All @@ -131,4 +164,4 @@ public void setOther(String other) {
this.other = other;
}
}
}
}
@@ -1,27 +1,26 @@
package com.vladmihalcea.hibernate.type;

import com.vladmihalcea.hibernate.type.util.Configuration;
import org.hibernate.EntityMode;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.*;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.type.BasicType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.ForeignKeyDirection;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.IncomparableComparator;
import org.hibernate.usertype.CompositeUserType;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;

/**
Expand All @@ -33,21 +32,22 @@
*
* @author Vlad Mihalcea
*/
public abstract class ImmutableCompositeType<T> implements CompositeUserType, BasicType {
public abstract class ImmutableCompositeType<T> implements CompositeUserType, CompositeType, BasicType {

private final Configuration configuration;

private final Class<T> clazz;

private final List<Method> clazzMethods;

/**
* Initialization constructor taking the {@link Class}
* and using the default {@link Configuration} object.
*
* @param clazz the entity attribute {@link Class} type to be handled
*/
protected ImmutableCompositeType(Class<T> clazz) {
this.clazz = clazz;
this.configuration = Configuration.INSTANCE;
this(clazz, Configuration.INSTANCE);
}

/**
Expand All @@ -59,6 +59,7 @@ protected ImmutableCompositeType(Class<T> clazz) {
protected ImmutableCompositeType(Class<T> clazz, Configuration configuration) {
this.clazz = clazz;
this.configuration = configuration;
this.clazzMethods = Collections.unmodifiableList(Arrays.asList(clazz.getMethods()));
}

/**
Expand Down Expand Up @@ -173,7 +174,7 @@ public boolean isAnyType() {

@Override
public boolean isComponentType() {
return false;
return true;
}

@Override
Expand Down Expand Up @@ -333,4 +334,59 @@ public String[] getRegistrationKeys() {
getName()
};
}

@Override
public Type[] getSubtypes() {
return getPropertyTypes();
}

@Override
public boolean[] getPropertyNullability() {
return new boolean[]{false, false};
}

@Override
public Object[] getPropertyValues(Object component, SharedSessionContractImplementor session) throws HibernateException {
return new Object[]{getPropertyValue(component, 0), getPropertyValue(component, 1)};
}

@Override
public Object[] getPropertyValues(Object component, EntityMode entityMode) throws HibernateException {
return new Object[]{getPropertyValue(component, 0), getPropertyValue(component, 1)};
}

@Override
public Object getPropertyValue(Object component, int index, SharedSessionContractImplementor session) throws HibernateException {
return getPropertyValue(component, index);
}

@Override
public void setPropertyValues(Object component, Object[] values, EntityMode entityMode) throws HibernateException {
throw new HibernateException("Calling setPropertyValues is illegal on on " + clazz.getName() + " because it's an immutable object!");
}

@Override
public CascadeStyle getCascadeStyle(int index) {
return CascadeStyles.NONE;
}

@Override
public FetchMode getFetchMode(int index) {
return FetchMode.DEFAULT;
}

@Override
public boolean isMethodOf(Method method) {
return clazzMethods.contains(method);
}

@Override
public boolean isEmbedded() {
return false;
}

@Override
public boolean hasNotNullProperty() {
return true;
}
}

0 comments on commit 7b02229

Please sign in to comment.