I use #Builder of lombok project, so consider I have this example:
#Builder
public class Client {
private #Getter #Setter Integer id;
private #Getter #Setter String name;
}
Which is equivalent to:
public class Client {
private #Getter #Setter Integer id;
private #Getter #Setter String name;
public static class Builder {
private Integer id;
private String name;
private Builder() {
}
public Builder id(final Integer value) {
this.id = value;
return this;
}
public Builder name(final String value) {
this.name = value;
return this;
}
public Client build() {
return new Client(this);
}
}
public static Client.Builder builder() {
return new Client.Builder();
}
private Client(final Builder builder) {
this.id = builder.id;
this.name = builder.name;
}
}
When I try to set all the the fields in one shot there are no problem:
public static void main(String[] args) {
Client client = Client.builder()
.id(123)
.name("name")
.build();
}
Output:
Client{id=123, name=name}
Now, consider I want to make it in multiple shots. For example:
public static void main(String[] args) {
Client client = Client.builder()
.id(123)//<-------------------------- Set just the id
.build();
client = Client.builder()
.name("name")//<--------------------- Set name
.build();
}
This logically return null for id:
Client{id=null, name=name}
Generally, without lombok, I solve this issue with adding a new constructor in the Builder class which take the same object:
public static class Builder {
// ...
public Builder(Client client) {
this.id = client.id;
this.name = client.name;
}
// ...
}
Then I pass my object to that constructor:
Client client = Client.builder()
.id(123)
.build();
client = new Client.Builder(client)//<---------------- Like this
.name("name")
.build();
This solves my issue, but I can't arrive to solve it with lombok. Is there any way to solve this issue?
You can use the toBuilder property to do that.
#Builder(toBuilder=true)
public class Client {
private #Getter #Setter Integer id;
private #Getter #Setter String name;
}
and then you can use it like that
public void main(String[] args){
Client client = Client.builder()
.id(123)
.build();
client = client.toBuilder()
.name("name")
.build();
}
Depending on your use case, you can also keep a reference to an existing builder. You can have multiple to the build method.
Disclosure: I am a lombok developer.
Related
I had a DTO that was using Lombok functionaliy as shown below.But now due to some requirement I had to extend my DTO to a parent class which looks like below.How can I do minimal change in my DTO to support that.I tried using #SuperBuilder annotation but it failed.
DTO Before:
#Getter
#ToString
#EqualsAndHashCode
#Builder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestMessage {
private final String name;
}
Parent Class that needs to be extended
#Data
#SuperBuilder(toBuilder = true)
#JsonDeserialize(builder = MyDTO.Builder.class)
public abstract class MyDTO implements Serializable {
#JsonIgnore private final ObjectMapper objectMapper = new ObjectMapper();
protected String myAccountId;
protected MyDTO() {}
public static int hashCode(Object... objects) {
return Arrays.deepHashCode(objects);
}
public static boolean equal(Object o1, Object o2) {
// implementation of equals method
}
public abstract String emitSerializedPayload() throws JsonProcessingException;
#JsonPOJOBuilder(withPrefix = "")
protected abstract static class Builder<T extends MyDTO, B extends Builder<T, B>> {
protected T dtoInstance;
protected B builderInstance;
public Builder() {
dtoInstance = createDtoInstance();
builderInstance = returnBuilderInstance();
}
protected abstract T createDtoInstance();
protected abstract B returnBuilderInstance();
public B myAccountId(String accountId) {
dtoInstance.myAccountId = accountId;
return builderInstance;
}
public T build() {
return dtoInstance;
}
}
}
I tried to build RequestMessageClass manually and it works fine but there are lot of classes in my application and I dont want to change them manually, how can I change my existing RequestMessage class with annotations or some minimum change to get it working.
This is what I tried but I am getting compilation error when doing this
RequestMessage.Builder().name(myName).myAccountId(myAcId).build();
What I tried is like shown below:
#Getter
#ToString
#EqualsAndHashCode(callSuper = true)
#SuperBuilder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestMessage extends MyDTO {
private final String name;
#Override
public String emitSerializedPayload() throws JsonProcessingException {
// TODO Auto-generated method stub
return null;
}
}
You shouldn't mix lombok #Builder with static inner Builder class. If it is possible to get rid of Builder class, the next code should work.
RequestMessage:
#Getter
#ToString
#EqualsAndHashCode(callSuper = true)
#SuperBuilder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestMessage extends MyDTO {
private final String name;
#Override
public String emitSerializedPayload() throws JsonProcessingException {
return null;
}
public RequestMessage(String myAccountId, String name) {
super(myAccountId);
this.name = name;
}
}
MyDTO:
#Data
#SuperBuilder(toBuilder = true)
public abstract class MyDTO implements Serializable {
#JsonIgnore private final ObjectMapper objectMapper = new ObjectMapper();
protected String myAccountId;
protected MyDTO() {}
public MyDTO(String myAccountId) {
this.myAccountId = myAccountId;
}
public static int hashCode(Object... objects) {
return Arrays.deepHashCode(objects);
}
public static boolean equal(Object o1, Object o2) {
// implementation of equals method
return false;
}
public abstract String emitSerializedPayload() throws JsonProcessingException;
}
Test:
#Test
void name() {
String myName = "myName";
String myAccountId = "myAccountId";
var request = RequestMessage.builder().name(myName).myAccountId(myAccountId).build();
System.out.println("request = " + request);
RequestMessage requestMessage = new RequestMessage(myAccountId, myName);
}
I have to write some junit test cases to check entity. I'm using postgres as my database.
My entity class
#Entity
#Table(name = "display")
public class Display {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String group;
public Display() {
}
public Display(Long id, String title, String grp) {
this.id = id;
this.title= title;
this.group= grp;
}
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return this.id;
}
public void setGroup(String id) {
this.group = id;
}
public String getGroup() {
return this.group;
}
public void settitle(String title) {
this.title = title;
}
public String gettitle() {
return this.title;
}
}
My repository
#Repository
public interface DisplayRepository extends CrudRepository<Display, Long> {
}
Interface
public interface IDisplayService {
List<Display> findAll();
}
Service class
#Service
public class DisplayService implements IDisplayService {
#Autowired
private DisplayRepository repository;
#Override
public List<Display> findAll() {
List<Display> d = (List<Display>) repository.findAll();
return d;
}
}
I tried writing junit test cases but I get Could'nt load Application. Whats the right way to write junit test cases for this?
This is the test case I wrote for service
folder : test/java/example/demo/Test.java
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource("classpath:conn.properties")
public class DisplayServiceTest {
#Value("${id}")
private String value;
#Mock
private DisplayRepository DisplayReps;
#InjectMocks
private DisplayService DisplayService;
#Test
public void whenFindAll_thenReturnProductList() {
Menu m = new Menu()
m.setId(value);
List<Display> expectedDisplay = Arrays.asList(m);
doReturn(expectedDisplay).when(DisplayReps).findAll();
List<Display> actualDisplay = DisplayService.findAll();
assertThat(actualDisplay).isEqualTo(expectedDisplay);
}
in test/java/example/demo/resources
conn.properties
id=2
Its returning 0 for value
Whats the issue?
Thanks
I have managed to make your code to work. I will post only the changed classes:
The interface:
public interface DisplayRepository extends CrudRepository<Display, Long> {
Optional<Display> findByTitle(String name);
}
The test class:
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class DisplayRepositoryTest {
#Autowired
private TestEntityManager testEntityManager;
#Autowired
private DisplayRepository productRespository;
#Before()
public void setUp(){
Display m = new Display();
// m.setId(2L); // The ID is autogenerated; can retrieve it from the persistAndFlush result
m.setCategory("Group1");
m.setTitle("Product2");
testEntityManager.persistAndFlush(m);
}
#Test
public void whenFindByName_thenReturnProduct() {
// when
Display product = productRespository.findByTitle("Product2").orElseThrow(() -> new RuntimeException("Product not found"));
// then
assertThat(product.getTitle()).isEqualTo("Product2");
}
#Test
public void whenFindAll_thenReturnProductList() {
// when
List<Display> products = (List<Display>) productRespository.findAll();
// then
assertThat(products).hasSize(1);
}
}
When trying to run the code you provided, there were a few issues:
you were using the reserved word group as a field in the Display class. Because of this, Hibernate couldn't create the table, so I renamed it to category.
there was a compilation issue because the method findByName wasn't defined in the repository; also, there was no field name in the Display class to which the mapping to be made; because of this, I've added the method findByTitle because it's an existing field and it seemed to match the value you queried in the test method.
because the ID field is autogenerated, the test setup() failed when persisting the Display.
If you want to use #Mock for mocking classes, you must call:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
You can then mock responses as usual: Mockito.when(DisplayReps.findByTitle("A")).thenReturn(Optional.of(new Display(2L, "ALFA", "GRP1")));
I have a Spring Boot application in which I have created an entity, a repository and a service.
I save entities in the database via transactions and everything works fine, my database is populated as I would expect. Also, I should mention that my database is created in PHPMyAdmin.
I also created a repository in order to fetch some data from the database by extending the Crud Repository. I also have a service which stores the methods that call the repository.
Though, none of the methods I have return anything ( my database is not empty ) and I do not know why. I have also tried adding #EnableJpaRepositories and #ComponentScan for the entity, but this did not work. Below are my classes:
The entity ( I will not put all the getters and setters here) :
#Entity
#Table(name = "matches", schema = "tennis", catalog = "")
public class MatchesEntity {
private int id;
private String namePlayer1;
private String namePlayer2;
private int setsPlayer1;
private int setsPlayer2;
private String odd1;
private String odd2;
private String competition;
private String surface;
private String status;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "Name_player1")
public String getNamePlayer1() {
return namePlayer1;
}
public void setNamePlayer1(String namePlayer1) {
this.namePlayer1 = namePlayer1;
}
#Basic
#Column(name = "Name_player2")
public String getNamePlayer2() {
return namePlayer2;
}
// other getter & setters
}
The repository:
#Repository
public interface MatchesRepository extends CrudRepository<MatchesEntity,
Integer> {
List<MatchesEntity> getAllBySurface(String surface);
}
The service:
#Service
public class MatchesService {
#Autowired
MatchesRepository matchesRepository;
public int countMatchesOnHard() {
return matchesRepository.getAllBySurface("hard").size();
}
public MatchesEntity findMatchById() {
return matchesRepository.findById(2378).get();
}
}
The main class:
#SpringBootApplication
#EnableJpaRepositories(basePackageClasses={MatchesRepository.class})
#EntityScan(basePackageClasses=MatchesEntity.class)
public class PicksApplication {
#Autowired
static MatchesService matchesService;
public static void main(String[] args) {
MatchesEntity matchesEntity = matchesService.findMatchById();
int numberOfMatchesOnHard = matchesService.countMatchesOnHard();
System.out.println(numberOfMatchesOnHard);
}
}
Any method I try which is repository related returns null.
Can anyone help me with a suggestion ?
Your main class PicksApplication is troublesome. The main method must trigger SpringApplication.run for the spring boot to initialize itself & the context for autowires to work. You are breaking all that within your code. You can utilize CommandLineRunner and add your code in run() method.
Like this;
#SpringBootApplication
public class PicksApplication implements CommandLineRunner {
#Autowired
private MatchesService matchesService;
public static void main(String[] args) {
SpringApplication.run(PicksApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
MatchesEntity matchesEntity = matchesService.findMatchById();
int numberOfMatchesOnHard = matchesService.countMatchesOnHard();
System.out.println(numberOfMatchesOnHard);
}
}
then it ought to work, rest of the code looks OK
I am trying to use RestTemplate to call a webservice, currently I am using the Object type rather than a concrete user defined one which is what I want to do.
Currently the response from the web service is:
{Locales=[{Code=ar-AE, Name=العربية (الإمارات العربية المتحدة)}, {Code=az-AZ, Name=Azərbaycanılı (Azərbaycan)}, {Code=bg-BG, Name=български (България)}]}
I am currently doing this:
Object locales = restTemplate.getForObject(localeUrl, Object.class, apiKey);
which is I want to be able to map it to a class that I have defined, but not sure how my class should be laid out, my class currently looks like this:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
#XmlRootElement(name = "Locales")
#XmlAccessorType(XmlAccessType.FIELD)
public class Locales {
private List<Locale> Locales = new ArrayList<>();
private Locales(){};
public List<Locale> getLocales() {
return Locales;
}
public void setLocales(ArrayList<Locale> newLocales) {
this.Locales = newLocales;
}
}
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Locale {
private String Code;
private String Name;
private Locale(){}
public String getCode() {
return this.Code;
}
public void setCode(String Code) {
this.Code = Code;
}
public String getName() {
return this.Name;
}
public void setName(String Name) {
this.Name = Name;
}
}
Use below code for calling API -
Locales locales = restTemplate.getForObject(localeUrl, Locales.class, apiKey);
Create one class Locales -
#XmlRootElement(name = "Locales")
#XmlAccessorType(XmlAccessType.FIELD)
public class Locales{
private List<Locale> locales = new ArrayList<>();
// getter and setter
}
Ok so I am new to spring and don't really know how this works. I have been trying a few things and think its close to doing it but not getting any data from the server and giving me this error
Unsatisfied dependency expressed through constructor argument with index 4 of type [jp.co.fusionsystems.dimare.crm.service.impl.MyDataDefaultService]: : Error creating bean with name 'MyDataDefaultService' defined in file
My end point
//mobile data endpoint
#RequestMapping(
value = API_PREFIX + ENDPOINT_MyData + "/getMyData",
method = RequestMethod.GET)
public MyData getMyData() {
return MyDataDefaultService.getData();
}
My Object
public class MyData {
public MyData(final Builder builder) {
videoLink = builder.videoLink;
}
private String videoLink;
public String getVideoLink()
{
return videoLink;
}
public static class Builder
{
private String videoLink = "";
public Builder setVideo(String videoLink)
{
this.videoLink = videoLink;
return this;
}
public MyData build()
{
return new MyData(this);
}
}
#Override
public boolean equals(final Object other) {
return ObjectUtils.equals(this, other);
}
#Override
public int hashCode() {
return ObjectUtils.hashCode(this);
}
#Override
public String toString() {
return ObjectUtils.toString(this);
}
}
The Repository
public classMyServerMyDataRepository implements MyDataRepository{
private finalMyServerMyDataJpaRepository jpaRepository;
private final MyDataConverter MyDataConverter = new MyDataConverter();
#Autowired
publicMyServerMyDataRepository(finalMyServerMyDataJpaRepository jpaRepository) {
this.jpaRepository = Validate.notNull(jpaRepository);
}
#Override
public MyData getData() {
MyDataEntity entity = jpaRepository.findOne((long) 0);
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
The DefaultService that gets called by the endpoint
public class MyDataDefaultService {
private static final Logger logger = LoggerFactory.getLogger(NotificationDefaultService.class);
private finalMyServerMyDataRepository repository;
#Autowired
public MyDataDefaultService(MyServerMyDataRepository repository) {
this.repository = Validate.notNull(repository);
}
//Get the data from the server
public MobileData getData()
{
logger.info("Get Mobile Data from the server");
//Get the data from the repository
MobileData mobileData = repository.getData();
return mobileData;
}
}
The Converter
public class MyDataConverter extends AbstractConverter<MyDataEntity, MyData>
{
#Override
public MyData convert(MyDataEntity entity) {
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
}
My Entity
#Entity
#Table(name = “myServer”)
public class MyDataEntity extends AbstractEntity{
#Column(name = "video_link", nullable = true)
private String videoLink;
public String getVideoLink() {
return videoLink;
}
public void setVideoLink(final String videoLink) {
this.videoLink = videoLink;
}
}
Thank you for any help with this
Hibernate entity should have default constructor defined and implement Serializable interface as well, assume AbstractEntity matches the requirement. Hibernate won't accept an entity without a primary key so you have to define the one too:
#Entity
#Table(name = “myServer”)
public class MyDataEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
#Column(name = "video_link", nullable = true)
private String videoLink;
public MyDataEntity() {
}
...setters&getters
}
MyData object represents the JSON server response, you can use Jackson annotations to control the result JSON properties:
public class MyDataResponse {
#JsonProperty("video_link")
private String videoLink;
public MyDataResponse() {
}
public MyDataResponse(String videoLink) {
this.videoLink = videoLink;
}
...setters&getters
}
Spring has an awesome project so called Spring Data that provides the JPA repositories, so there's no even the #Repository annotation ever needed:
public class MyDataRepository extends CrudRepository<MyDataEntity, Long> {
}
The Builder class represents the Service layer:
#Service
public class MyDataService {
#Autowired
private MyDataRepository myDataRepository;
public MyDataResponse getMyData(Long id) {
MyDataEntity entity = myDataRepository.findOne(id);
...rest logic, copy necessary data to MyDataResponse
}
}
Then a controller is:
#RestController // #ResponseBody not needed when using like this
public MyDataController {
#Autowired
private MyDataService myDataService;
#RequestMapping("/getMyData") // no need to specify method for GET
public MyDataResponse getMyData(#RequestParam("ID") Long myDataId) {
... validation logic
return myDataService.getMyData(myDataId); // return response
}
}
Now it should work, don't forget to add required dependencies to your classpath.