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());
}
}
Related
I created a Bookings class (Model Class) for the table "Bookings" in my MySQL DB, All the data members in this class are set in accordance with the database table.
All the GET requests work, only the POST requests return error.
Repository Interface :
package com.example.demo.bookings;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface BookingsRepository extends CrudRepository<Bookings, Integer>{
Iterable<Bookings> findAllByBookingName(String name);
}
BookingsController class :
package com.example.demo.bookings;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class BookingsController {
#Autowired
BookingsService bs;
#RequestMapping("/bookings")
public List<Bookings> getBookings(){
return bs.getAllBookings();
}
#RequestMapping("/{name}/bookings")
public List<Bookings> getBookings(#PathVariable String name){
return bs.getBookingsByName(name);
}
#PostMapping("/bookings")
public void addBookings(Bookings bookings) {
bs.addBookings(bookings);
}
}
Bookings POJO class :
package com.example.demo.bookings;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Bookings {
#Id
int bookingId;
String bookingName;
int bookingPid;
int passengers;
int cost;
public Bookings() {
}
public Bookings(int bookingId, String bookingName, int bookingPid, int passengers, int cost) {
super();
this.bookingId = bookingId;
this.bookingName = bookingName;
this.bookingPid = bookingPid;
this.passengers = passengers;
this.cost = cost;
}
public int getBookingId() {
return bookingId;
}
public void setBookingId(int bookingId) {
this.bookingId = bookingId;
}
public String getBookingName() {
return bookingName;
}
public void setBookingName(String bookingName) {
this.bookingName = bookingName;
}
public int getBookingPid() {
return bookingPid;
}
public void setBookingPid(int bookingPid) {
this.bookingPid = bookingPid;
}
public int getPassengers() {
return passengers;
}
public void setPassengers(int passengers) {
this.passengers = passengers;
}
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
}
Edit 2:
As suggested, I changed the Model Class datamembers to camelcase for columns having '_' in their name. This helped resolve much of the problems I was facing but the POST method still returns error.
The error I get when I try a POST request :
java.sql.SQLIntegrityConstraintViolationException: Column 'booking_name' cannot be null
In your TopicService you are trying to autowire TopicsRepository, but you don't have any bean of type TopicsRepository.
Add following annotation:
#Repository
public interface TopicsRepository extends CrudRepository<Topics, String>{
}
The underscore-separated fields in the database are usually mapped to camelCase fields in the classes. The field name in the Bookings class should be bookingName, and the repository method signature should be:
Iterable<Bookings> findAllByBookingName(String bookingName);
Same for all other fields in the Bookings class.
I am writing test case for mapper method where new object instance is creating for mapping. How to mock all mapper objects and set in setter? Eclipse tool passing test but jacoco is giving 0% coverage on this code.
public class NewUseInsideMethods {
public StudentOldTestingReturn OldLogicTesting(StudentOldTesting Student) {
StudentOldTestingReturn sturentReturn = new StudentOldTestingReturn();
OldBatchName batchName = new OldBatchName();
batchName.setId("1");
batchName.setName("test");
sturentReturn.setId(batchName.getId());
sturentReturn.setName(Student.getName());
return sturentReturn;
}
}
public class OldBatchName {
private String name;
private String id;
//setter and getter
}
public class StudentOldTesting {
private String name;
private String id;
//setter and getter
}
public class StudentOldTestingReturn {
private String name;
private String id;
//setter and getter
}
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
class NewUseInsideMethodsTest {
#Test
void OldLogicTesting() throws Exception {
StudentOldTesting oldTesting = new StudentOldTesting();
oldTesting.setName("shraban");
oldTesting.setId("1");
NewUseInsideMethods insideMethods = new NewUseInsideMethods();
StudentOldTestingReturn result = insideMethods.OldLogicTesting(oldTesting);
Assertions.assertNotNull(result);
Assertions.assertEquals("shraban", result.getName());
Assertions.assertEquals("1", result.getId());
}
}
I'm new to Java streams.
I have an Array of n classes.
The classes have several fields with a particular annotation (SomeAnnotationClass.class)
I'm trying to get a Set of all the fields annotations values which are annotated with this particular annotation. If the field does not have the annotation I want the name of the field.
So i tried something like this:
Stream.of(clazzes).map( c ->
Stream.of((c.getDeclaredFields()))
.map(
field ->
Optional.ofNullable(
field.getDeclaredAnnotation(SomeAnnotationClass.class).value())
.orElse(field.getName())).collect(Collectors.toSet())).collect(Collectors.toSet());
2 issues with this:
I get a Set<Set> instead of Set due to collecting 2 times.
I get a Nullpointer if the annotation is not present but SomeAnnotationClass.class.value() is called
Can I achieve this elegantly with streams?
A set of sets should be flattened:
// in Main.java
public static Set<String> getValuesOrNames(Class ... clazzes) {
return Arrays.stream(clazzes) // convert array to Stream<Class>
.flatMap(c -> Arrays.stream(c.getDeclaredFields())) // convert array of fields Stream<Field>
.map(field -> Optional.ofNullable(field.getAnnotation(SomeAnnotationClass.class))
.map(SomeAnnotationClass::value) // assuming SomeAnnotationClass has value method
.orElse(field.getName())
)
.collect(Collectors.toSet());
}
Test
// annotation class
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface SomeAnnotationClass {
String value() default "";
}
import java.util.*;
import java.util.stream.Collectors;
import lombok.Data;
public class Main {
public static void main(String[] args) {
System.out.println(getValuesOrNames(Something.class, Main.class));
}
#Data
public static class Something {
#SomeAnnotationClass(value = "String foo")
private String foo;
#SomeAnnotationClass
private String emptyFoo;
private String bar;
#SomeAnnotationClass(value = "int id")
private int id;
}
}
Output
[, String foo, bar, int id]
As mentioned by #Andy Turner, you can use to flatMap to map multiple streams into single stream and to avoid NPE check the annotation before accessing value()
Set<String> value = clazzes.stream().map(c -> Stream.of((c.getDeclaredFields()))
.map(field -> Optional.ofNullable(
field.getDeclaredAnnotation(SomeAnnotationClass.class)).map(SomeAnnotationClass::value).orElseGet(field::getName)).collect(Collectors.toSet()))
.flatMap(Collection::stream).collect(Collectors.toSet());
package io.falcon.instagram.indexer.util;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.Enumerated;
import javax.persistence.Id;
public class Example {
public static void main(String[] args) {
List<Class<?>> classes = List.of(Test.class);
Class<Enumerated> someAnnotationClass = Enumerated.class;
Set<String> fieldNames =
classes.stream()
.flatMap(c -> Arrays.stream(c.getDeclaredFields().clone())) // because getDeclaredFields returns array type
.map((Field field) -> Optional.ofNullable(field.getDeclaredAnnotation(someAnnotationClass)).map(a -> field.getName()))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
System.out.println(fieldNames);
}
public static class Test {
#Id
private final String id;
#Id
#Enumerated
private final String field;
#Enumerated
private final String another;
#Enumerated
private final String theGame;
public Test(String id, String field, String another, String theGame) {
this.id = id;
this.field = field;
this.another = another;
this.theGame = theGame;
}
}
}
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?
I'm just getting familiar with Jackson binding. However, when I'm testing setSerializationInclusion(JsonInclude.Include.NON_NULL), I found that it's not working sometimes.
Here is my code
package com.blithe.main;
import com.blithe.model.Student;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Jackson_2_NullValue {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Student s = new Student();
String stundetString = mapper.writeValueAsString(s);
System.out.println(stundetString);
// exclude null fields
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
s.setName("ss");
stundetString = mapper.writeValueAsString(s);
System.out.println(stundetString);
}
}
and the POJO
package com.blithe.model;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
// #JsonIgnoreProperties(ignoreUnknown = true)
// exclude null fields for the whole class
// #JsonInclude(Include.NON_NULL)
public class Student {
// exclude the field whe it's empty ("")
// #JsonInclude(value=Include.NON_EMPTY)
private String name;
private Integer age;
private Date birth;
// Jackson ignores it
#JsonIgnore
private String nickName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
}
the output is
{"name":null,"age":null,"birth":null}
{"name":"ss","age":null,"birth":null}
The later one should be null-value excluded, but it doesn't.
However, when I put my code this way.
package com.blithe.main;
import com.blithe.model.Student;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Jackson_2_NullValue {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
Student s = new Student();
String stundetString = mapper.writeValueAsString(s);
System.out.println(stundetString);
// exclude null fields
// mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
s.setName("ss");
stundetString = mapper.writeValueAsString(s);
System.out.println(stundetString);
}
}
It works with the output below
{}
{"name":"ss"}
Is this normal or just some kind of bug? Do I miss anything? The only maven dependency is jackson-databind 2.7.4. Any discussion is welcomed. Thanks!
Do not change ObjectMappers settings while using it. Once mapper has been in use not all settings take effect, because of caching of serializers and deserializers.
Configure an instance once and do not change settings after first use. It is done this way for thread-safety and performance.
Update: Dead links replaced with archive.org ones
http://wiki.fasterxml.com/JacksonFAQThreadSafety
http://wiki.fasterxml.com/JacksonBestPracticesPerformance
So the point is if you are using ObjectMappers at multiple places, try not to create objects again and again. it takes the configs of first initialized.
if you keep changing on a global level it will not work.