I can't understand why it's happening on a compareTo method using CompareToBuilder from Apache Commons Lang 3 and how may I solve it?
The class is as follow:
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.CompareToBuilder;
#Data
#EqualsAndHashCode(of = {"segno", "causale", "importi"})
public class SaraReportAggregate implements Comparable<SaraReportAggregate> {
private final String segno;
private final String causale;
private final AggregatoSara importi = new AggregatoSara();
#Data
public static class AggregatoSara implements Comparable<AggregatoSara> {
private long importoTotale;
private long importoTotaleContanti;
private long numeroTotaleOperazioni;
private long numeroTotaleOperazioniContanti;
#Override
public int compareTo(AggregatoSara other) {
return new CompareToBuilder()
.append(other.importoTotale, this.importoTotale)
.append(other.numeroTotaleOperazioni, this.numeroTotaleOperazioni)
.append(other.importoTotaleContanti, this.importoTotaleContanti)
.append(other.numeroTotaleOperazioniContanti, this.numeroTotaleOperazioniContanti)
.toComparison();
}
}
#Override
public int compareTo(SaraReportAggregate other) {
return new CompareToBuilder()
.append(this.importi, other.importi)
.append(this.causale, other.causale)
.append(this.segno, other.segno)
.toComparison();
}
}
Calling Collections#sort(List) on a list of SaraReportAggregate happened to throw
java.lang.IllegalArgumentException: Comparison method violates its general contract!
What should I do?
Related
I'm developing a service oriented platform for retrieving, creating and updating entities from DB.
The point here is that every single java entity extends AbstractEntity, so for example, I have,
MyCar extends AbstractEntity implements Serializable
This AbstractEntity has common fields (such as id or audit ones).
So I have already developed a GenericReadService that, receiving a classname and a parameterMap, can read any entity and creates a EntityActionResult<T> including a List<T extends AbstractEntity>.
My problem comes when trying to transform that T type into something like <K extends GenericDTO>, as the client asking doesn't know AbstractEntity (obvious) but only GenericDTO. Doing this for safety and modularization.
So, now, I'm stuck on transforming the ListResponse<T> to a ReturnList<K extends GenericDTO>, as I don't find the way for knowing which K class should apply for each T.
This is what I actually have written:
private GenericEntityActionResult transform (EntityActionResult result) {
AsnGenericEntityActionResult r = new AsnGenericEntityActionResult();
if (result.getCode() == EntityActionResult.Code.ENTITY || result.getCode() == EntityActionResult.Code.ENTITY_LIST ) {
r.setCode(AsnGenericEntityActionResult.Code.OK);
List <? extends GenericDTO> list = new ArrayList<>();
if (result.getEntity() != null) {
//transform this entity into DTO
//<b>SOMETHING LIKE list.add(result.getEntity());</b>
} else if (result.getList() != null && !result.getList().isEmpty()) {
for (AbstractEntity a:result.getList()) {
//transform this entity into DTO
//<b>SOMETHING LIKE list.add(result.getEntity());</b>
//list.add(result.getEntity());
}
}
r.setList(list);
}
return r;
I´m obviously stuck on the SOMETHING LIKE, but can't find a proper way of doing so.
I thought about creating a abstract <T extends GenericDTO> transformToDTO() method on AbstractEntity, but can't do it since there are lots (and i mean hundreds) of Entities extending AbstractEntity and this client-service approach is a new development for some Entities, not the whole system.
Any clever ideas?
You can try to use the Java Introspection API or some more robust library on top of this API like apache commons beanutils or even more powerful bean mapping library like Dozer or something newer see
Following example demonstrating the basic technique, only raw introspection and reflection with two compatible POJO beans.
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
class Tranformation {
public static void main(String[] args) {
MyCar car = new MyCar();
car.setId("id01");
car.setName("Komodo");
car.setMadeIn("Jakarta");
CarDTO dto = toDto(CarDTO.class, car);
System.out.println(dto);
}
public <E extends AbstractEntity, D extends GenericDTO> D toDto(Class<D> dtoClass, E entity) {
if (null == entity) {
throw new NullPointerException("Entity can not be null");
}
try {
final D ret = dtoClass.newInstance();
BeanInfo dtoBeanInfo = Introspector.getBeanInfo(dtoClass);
final Map<String, PropertyDescriptor> mapping = Arrays.stream(dtoBeanInfo.getPropertyDescriptors())
.collect(Collectors.toMap(PropertyDescriptor::getName, Function.identity()));
final BeanInfo entityBeanInfo = Introspector.getBeanInfo(entity.getClass());
Arrays.stream(entityBeanInfo.getPropertyDescriptors()).forEach(src -> {
if (!"class".equals(src.getName())) {
PropertyDescriptor dst = mapping.get(src.getName());
if (null != dst) {
try {
dst.getWriteMethod().invoke(ret, src.getReadMethod().invoke(entity, null));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
}
});
return ret;
} catch (InstantiationException | IntrospectionException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
public static class GenericDTO {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class CarDTO extends GenericDTO {
private String madeIn;
public String getMadeIn() {
return madeIn;
}
public void setMadeIn(String madeIn) {
this.madeIn = madeIn;
}
#Override
public String toString() {
return String.format("CarDTO [id=%s, name=%s, madeIn=%s]", getId(), getName(), madeIn);
}
}
public static class AbstractEntity implements Serializable {
private static final long serialVersionUID = 70377433289079231L;
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class MyCar extends AbstractEntity {
private static final long serialVersionUID = 8702011501211036271L;
private String madeIn;
public String getMadeIn() {
return madeIn;
}
public void setMadeIn(String madeIn) {
this.madeIn = madeIn;
}
}
}
Outputs:
CarDTO [id=id01, name=Komodo, madeIn=Jakarta]
I have a Object:
public class ListAnnotationsOrigin implements Serializable {
/**
*
*/
private static final long serialVersionUID = 8443084209405942551L;
#JsonProperty("opcombo")
private List<opcombo> listComboOp;
#JsonProperty("vicombo")
private List<vicombo> listComboVi;
#JsonProperty("vicombo")
private String vicombo;
#JsonProperty("viversion")
private String idVersionVi;
#JsonProperty("closed")
private String closed;
#JsonProperty("params")
private List<SegmentOrigin> listSegmentOrigin; // <-- here
// getters,setters etc.
I want sort listSegmentOrigin
#Entity
#Table(name = "SegmentOrigin")
public class SegmentOrigin implements Comparable<T>{ // <-- ERROR
....
#JsonProperty("idSegment")
private String idSegment;
public int compareTo(SegmentOrigin arg0) {
if (this.getIdSegment().compareTo(arg0.getIdSegment())) { // <<--ERROR type mismatch: cannot convert from int to boolean
return 1;
} else {
return 0;
}
}
}
In my controller->
ListAnnotationsOrigin list = this.getListAnnotationsOrigin ();
return Collections.sort((List<SegmentOrigin>) list.getListSegmentOrigin());
I send my List from ListAnnotationsOrigin in an orderly manner, Then when I implements Comparable{ I get red line "implements methods unics"
I import -> public int compareTo(SegmentOrigin arg0) { ... and I get other error, thanks.
Your entity should look like this :
#Entity
#Table(name = "SegmentOrigin")
public class SegmentOrigin implements Comparable<SegmentOrigin>{
#JsonProperty("idSegment")
private String idSegment;
public int compareTo(SegmentOrigin arg0) {
return this.getIdSegment().compareTo(arg0.getIdSegment()); // Check nullity if needed
}
}
implements Comparable<SegmentOrigin> means that your object can be compared with another SegmentOrigin object.
Method 1: traditional getter/setter
Toyota class:
public class ToyotaCar implements Serializable {
private static final long serialVersionUID = 2011932556974180375L;
private int miles;
public void addMiles(int miles){
this.miles = miles;
}
public int getMiles(){
return miles;
}
}
Human class:
public class Human implements Serializable {
private static final long serialVersionUID = 1748193556974180375L;
private ToyotaCar car;
public void setCar(ToyotaCar car){
this.car = car;
}
public int getCar(){
return car;
}
public void addCarMiles(int num){
getCar().addMiles(num);
}
}
Method 2: "other"
Toyota class: -same as above toyota class-
Additional containerHandler class:
public enum HumanContentsContainer {
CAR{
#Override public Object getContainer(){
return new ToyotaCar();
}
},
HOUSE;
public Object getContainer(){ //because cannot be static enum constant as every human has different items
return null;
}
}
Human class:
public class Human implements Serializable {
private static final long serialVersionUID = 1748193556974180375L;
private HashMap<HumanContentsContainer, Object> contents;
public void setContents(){
for (HumanContentsContainer c : HumanContentsContainer.values()){
contents.put(c, c.getContainer());
}
}
public HashMap<HumanContentsContainer, Object> getContents(){
return contents;
}
public void addCarMiles(int num){
//TODO how to replicate this: getCar().addMiles(num);???
}
//TODO i dont want to use the below method because whats the point of creating a whole container handler if im just going to use a traditional getter again?
//public ToyotaCar getCar(){
// return (ToyotaCar) contents.get(HumanContentsContainer.CAR);
// }
}
So how do I replicate the getCar().addMiles(x) method using a traditional getter without actually creating a getter?
Please note I also don't want to do this (below code): Because again, not worth it over a getter then:
public void addCarMiles(int num){
((ToytotaCar)contents.get(HumanContentsContainer.CAR).addMiles(num);
}
Looking for some easy kind of usage like:
human.getContentsThatIsIntanceOf(ToyotaCar).addMiles(1);
But don't know what getContentsThatIsInstanceOf would look like
I would go with:
public class Human implements Serializable {
private static final long serialVersionUID = 1748193556974180375L;
private ToyotaCar car;
public void setCar(ToyotaCar car){
this.car = car;
}
public int getCar(){
return car;
}
public void addCarMiles(int num){
getCar().addMiles(num);
}
public Map<HumanContentsContainer, Object> getContents(){
Map<HumanContentsContainer, Object>map = new HashMap();
map.put(CAR,this.car );
//same for all the shoes and clothes and whatever the Human has
}
public void setContents(){
for (HumanContentsContainer c : HumanContentsContainer.values()){
switch (c){
case CAR:{
this.car=c.getContainer();
}
}
//and so on
}
}
}
Edit
If you need to have a dynamic set of capabilities, I would suggest that you indeed keep the map of objects, and get rid of the ‘addCarMiles‘ method, as it implies that every human has a car.
I would implement public method on human ‘performCommand(CapabilityType, CapabilityCommand)‘ where the command will receive the capability and perform the operation on it. You may check out the Command Pattern tutorials.
Edit 2:
If all you want is to create a getter which will return dynamic type, you can use generics.
import java.io.Serializable;
import java.util.HashMap;
import java.util.NoSuchElementException;
public class Human implements Serializable {
private static final long serialVersionUID = 1748193556974180375L;
private HashMap<Class, Object> contents;
public void setContents(){
for (HumanContentsContainer c : HumanContentsContainer.values()){
contents.put(c.getContainer().getClass(), c.getContainer());
}
}
public HashMap<Class, Object> getContents(){
return contents;
}
public <T> T getContentsThatIsIntanceOf(Class<T> type){
Object object = contents.get(type);
if (object==null){
throw new NoSuchElementException("No such element: "+type.getName());
}
return type.cast(object);
}
public void usageExample(){
this.getContentsThatIsIntanceOf(ToyotaCar.class).addMiles(10);
}
}
I have the following structure:
public abstract class BaseVersionedEntity {
private long id;
private List<BaseRevision<? extends BaseVersionedEntity>> versions;
public BaseRevision<? extends BaseVersionedEntity> getLatestRevision() {
return versions.get(versions.size() - 1);
}
public abstract BaseRevision<? extends BaseVersionedEntity> newRevision();
}
public abstract class BaseVersionedEntityData<E> {
private long id;
private BaseRevision<E> revision;
}
public abstract class BaseRevision<E> implements Comparable<BaseRevision<E>> {
private long id;
private Timestamp timestamp;
private E versionedEntity;
private BaseVersionedEntityData<E> versionedEntityData;
public BaseVersionedEntityData<E> getVersionedEntityData() {
return versionedEntityData;
}
}
That will be implemented by this:
public class PersonEntity extends BaseVersionedEntity {
#Override
public BaseRevision<? extends BaseVersionedEntity> newRevision() {
PersonRevision newRevision = new PersonRevision();
newRevision.setTimestamp(new Timestamp(System.currentTimeMillis()));
getRevisions().add(newRevision);
return newRevision;
}
}
public class PersonData extends BaseVersionedEntityData<PersonEntity> {
}
public class PersonRevision extends BaseRevision<PersonEntity> {
}
Somewhere in my code i'll do the following call:
// is not null
PersonEntity personEntity;
PersonData personData = personEntity.getLatestRevision().getVersionedEntityData();
Out of some reasons that is marked with a type mismatch...
Type mismatch: cannot convert from BaseVersionedEntityData<capture#1-of ? extends BaseVersionedEntity> to PersonData
Can anyone find a mistake?? Or have any hints??
Thank you!!
Benjamin
The method getLatestRevision does not return a PersonRevision, it returns a BaseRevision, and even then PersonRevision doesn't return PersonData - you'll need an explicit cast since this is a downcast, and not even one of the "safe" caused-by-type-erasure downcasts:
PersonData personData = (PersonData)(personEntity.getLatestRevision().getVersionedEntityData());
I read that it's useful to use builder pattern when you have a class with a lot of parameters. I wonder how you can implement an entity using builder pattern. It would be great if you can provide sample code.
Of course it is possible, you just have to provide a (possibly nested) Builder for every Entity.
Here is a working example:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class FluentEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String someName;
private int someNumber;
private boolean someFlag;
protected FluentEntity(){}
private FluentEntity(String someName, int someNumber, boolean someFlag) {
this.someName = someName;
this.someNumber = someNumber;
this.someFlag = someFlag;
}
public long getId() {
return id;
}
public String getSomeName() {
return someName;
}
public int getSomeNumber() {
return someNumber;
}
public boolean isSomeFlag() {
return someFlag;
}
public static FluentEntityBuilder builder() {
return new FluentEntityBuilder();
}
public static class FluentEntityBuilder {
private String someName;
private int someNumber;
private boolean someFlag;
public FluentEntityBuilder setSomeName(final String someName) {
this.someName = someName;
return this;
}
public FluentEntityBuilder setSomeNumber(final int someNumber) {
this.someNumber = someNumber;
return this;
}
public FluentEntityBuilder setSomeFlag(final boolean someFlag) {
this.someFlag = someFlag;
return this;
}
public FluentEntity build() {
return new FluentEntity(someName, someNumber, someFlag);
}
}
}
The code to use it would be this:
FluentEntity entity = FluentEntity.builder().setSomeName(someName).setSomeNumber(someNumber)
.setSomeFlag(someFlag).build();
Just keep in mind that you have to exclude auto-generated fields like the primary key (in this example id) if you have some.
If you want to get rid of the "boilerplate" code for creating Builder classes for every Entity I would recommend a convenience library, something like Lombok. Then you will get your Builders (and even more) by just annotating your Entites, maybe it costs a little extra work to exclude the id fields.
You should take a look at Project Lombok
Nevertheless, here is some code to test this Builder (implemented with Spring Boot and Hibernate).
The repository:
import org.springframework.data.repository.CrudRepository;
import com.example.model.FluentEntity;
public interface FluentEntityRepository extends CrudRepository<FluentEntity, Long> {
}
And here are some tests:
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import java.util.stream.StreamSupport;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import com.example.model.FluentEntity;
#RunWith(SpringRunner.class)
#Transactional
#SpringBootTest
public class FluentEntityRepositoryTests {
#Autowired
private FluentEntityRepository fluentEntityRepository;
#Test
public void insertAndReceiveFluentEntityCreatedWithBuilder() {
final String someName = "name";
final int someNumber = 1;
final boolean someFlag = true;
FluentEntity entity = FluentEntity.builder().setSomeName(someName).setSomeNumber(someNumber)
.setSomeFlag(someFlag).build();
entity = fluentEntityRepository.save(entity);
assertThat("Entity did not get a generated Id!", entity.getId(), greaterThan(-1L));
assertThat("Entity name did not match!", entity.getSomeName(), is(someName));
assertThat("Entity number did not match!", entity.getSomeNumber(), is(someNumber));
assertThat("Entity flag did not match!", entity.isSomeFlag(), is(someFlag));
}
#Test
public void insertSomeAndReceiveFirst() {
fluentEntityRepository.save(FluentEntity.builder().setSomeName("A").setSomeNumber(1).setSomeFlag(true).build());
fluentEntityRepository
.save(FluentEntity.builder().setSomeName("B").setSomeNumber(2).setSomeFlag(false).build());
fluentEntityRepository.save(FluentEntity.builder().setSomeName("C").setSomeNumber(3).setSomeFlag(true).build());
final Iterable<FluentEntity> findAll = fluentEntityRepository.findAll();
assertThat("Should get some iterable!", findAll, notNullValue());
final FluentEntity fluentEntity = StreamSupport.stream(findAll.spliterator(), false).findFirst().get();
assertThat("Should get some entity!", fluentEntity, notNullValue());
}
}