Using Spring data I would like to be able to define a custom get-method inside a domain model class without affecting the model itself. For example, using this model:
#Document
public class Person
{
private String firstName;
private String lastName;
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
}
Eveything is working fine so far: the model Person has the fields 'firstName' and 'lastName' and I can successfully save a 'person'. The resulting JSON has the fields 'firstName' and 'lastName'. Now I would like to add some additional data in the JSON without affecting the model and its save-operations, something like this:
#Document
public class Person
{
private String firstName;
private String lastName;
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
// custom method
public String getFullName()
{
return firstName+" "+lastName;
}
}
The JSON should contain the same data as before, but this time also an additional 'fullName'-field. However, at the same time the data model assumes an additional field 'fullName' is added and filled with null-values when saving into the database.
I have already tried annotations like #Transient, but this does not work. The documentation states "by default all private fields are mapped to the document, this annotation excludes the field where it is applied from being stored in the database", so it only can be applied to private fields in the class, not to get-methods.
What is the correct way to do this in Spring? Of course I can extend the class Person and include the getFullName-method there, but I was wondering if it is possible to include everything in one class.
Edit:
I use Elasticsearch as DB engine using spring-data-elasticsearch 1.2.0.RELEASE. I have just tested MongoDB as alternative and then it is working fine, even without the #Transient annotation. I think the index-method of the ElasticsearchRepository is serializing the provided class instance when saving it to the database. In that way the JSON-output and the saved data are always identical. Any suggestions?
Related
I am using jackson 2.10.0 (https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core/2.10.0), following is a simple test case
The Person class is defined as follows, for the setters, I have used the #JsonSetter annotation, and didn't use #JsonGetter for the getters,
import com.fasterxml.jackson.annotation.JsonProperty;
public class Person {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
#JsonSetter("first_name")
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
#JsonSetter("last_name")
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Then, I create a Person object ,and serialize it as string,
import com.fasterxml.jackson.databind.ObjectMapper;
public class Person3Test2 {
public static void main(String[] args) throws Exception {
Person p = new Person();
p.setFirstName("abc");
p.setLastName("def");
String str = new ObjectMapper().writeValueAsString(p);
System.out.println(str);
}
}
It will call Person's getters, since it doesn't use #JsonGetter, so I think the output should be
{"firstName":"abc","lastName":"def"}
But, I am surprised to find that it is :
{"first_name":"abc","last_name":"def"}
It looks that the #JsonSetter has affected the getter output, I would ask what's the behavior here.
#JsonSetter will effect during serialization also here is the github issue, if you want different name just use another annotation #JsonGetter on get method
Documentation may be wrong; #JsonSetter does not only affect deserialization. While it can indeed be used for asymmetric naming (similar to #JsonProperty itself with "split" annotation), its scope is not limited.
It may have been at some point, but after unification of property handling (in 1.8 or so), there is less separation between various property accessors.
I can review Javadocs to make it clear that none of annotations is strictly limited in scope -- some may only be relevant to one or the other, but none is intentionally separated.
I am new to spring boot. I want to achieve relaxed binding in spring boot. As per this documentation https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-relaxed-binding.
It says, even though if we have name with dashes(like first-name) in .properties file, it can be mapped to variable without dashes(like firstName). But it didn't seems to be working.
I have application.properties file like below:
person.first-name=orcl
person.address=xyz
And my Properties util java file looks like:
#ConfigurationProperties(prefix="person")
#Component
#PropertySource("file: application.properties")
public class ApplicationPropertiesUtil
{
private String firstName;
private String address;
public String getfirstName()
{
return firstName;
}
public void setfirstName(String firstName)
{
this.firstName = firstName;
}
public String getaddress()
{
return address;
}
public void setaddress(String address)
{
this.address = address;
}
}
address property is getting bind properly, but for firstname it is null.
The problem is your setter methods which don't align with java bean standard.
It should be named "setFirstName" with an upper case F.
Im still quite new into Java and I'm trying to set new value to specific field into existing object in arraylist.
I've got one class:
public class Client
private String firstName;
private String lastName;
//skip setters and getters
I added objects to arraylist using
listOfClients.add
and now I'd like to update only lastName in one of existing objects. To pick one of all objects I'm using index of Arraylist but still have no idea how can I update only this one spcific value. So far i've tried
listOfClients.set
but it didn't go well.
Could anyone tell me if there is any way to update only one, specific field in my object?
You should use the list's get method.
The get method returns the element at the specified position in this
list.
Use it as follows:
listOfClients.get(clientIndex).setFirstName(newFirstName);
Since you know the index, you can try listOfClients.get(index).setLastName("value").
listOfClients.get(index) gets you the object. Then you can set any value with it's setter method.
public class Test {
public static void main(String[] args) {
List<Client> clientList=new ArrayList<Client>();
Client c1=new Client();
c1.setFirstName("First");
c1.setLastName("FirstLast");
clientList.add(c1);
System.out.println("First: "+clientList.get(0).getFirstName() +"-"+ clientList.get(0).getLastName());
//Modify by setter
clientList.get(0).setLastName("ModifyLast");
System.out.println("After"+clientList.get(0).getFirstName() +"-"+ clientList.get(0).getLastName());
}
public static class Client{
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
}
find the index of the value and set it using: list.set( index_of_the value_to change, "value_to_update" );
Here is the example for your question i am updating the cow value here:
ArrayList nums = new ArrayList<>();
nums.add("Rat");
nums.add("cow");
nums.add("rabbit");
System.out.println(nums);
nums.set(1, "updated");
System.out.println(nums);
I wan't to be able to store computed values from Java in my database.
For example, I might have a Person class that with a firstName and a lastName. I may want a getter that returns the the total length of the Persons name, without it being an actual property.
#Entity
public class Person extends Model {
#Id
public Long id;
public String firstName;
public String lastName;
public Int getNameLength() {
return firstName.length() + lastName.length();
}
public Person (String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
So if I create a new Person like so:
Person bob = new Person("Bob", "Roy");
bob.save();
Then we should end up with this in the table:
| id | first_name | last_name | name_length |
====================================================
| 1 | "Bob" | "Roy" | 6 |
Does anyone know if this is possible?
Please for the sake of Old Gods and the New Gods don't do this
Doing something like this would totally mess up your database. You will run into problems sooner or later. Looking at your profile you obtained a CS degree so you definitely had your databases course. Remember the Second normal form and the Third normal form and how you will break this if you have attributes which depend on other attributes.
What you should do is to have either a transient field (marked with #Transient) or you can use the getter and provide the information from there. Every time you need to access the name_length you will call this getter but you won't store the information in the database.
Even if you want to calculate the length outside of your application, you can still use some database function for this - like length.
Edit based on the requirement mentioned by the OP:
In JPA there are two ways how you can declare the columns - either on fields or on methods (getters/setters). It would go like this:
#Column(name = "complex_calculation") // Due to some bad requirement
public Integer getNameLength() {
return fisrtName.length() + lastName.length();
}
However you mentioned Ebean in your question and Ebean is not regarded as the reference JPA implementation. There is a good chance this is not yet supported but you can try it in your specific case.
There is another way which is proven to work. You define your model like this:
#Entity
public class Person extends Model {
#Id
private Long id;
private String firstName;
private String lastName;
private Integer nameLength;
public Long getId() {
return id;
}
// getter for first name and last name with #Column annotation
#Column(name = "complex_calculation")
public Integer getNameLength() {
return firstName.length() + lastName.length();
}
public Person (String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
updateComplexCalculation();
}
public void setFirstName(String firstName) {
this.firstName = firstName;
updateComplexCalculation();
}
public void setLastName(String lastName) {
this.lastName = lastName;
updateComplexCalculation();
}
private void updateComplexCalculation() {
this.nameLength = firstName.length() + lastName.length();
}
}
The important part is the updateComplexCalculation method. When the constructor is called and on every setter-call you invoke this method to update the complex property. Of course you should invoke it only on setter-calls which are needed for the computation.
The following code:
Person p = new Person("foo", "bar");
p.save();
Logger.debug("Complex calculation: " + p.getNameLength());
p.setFirstName("somethingElse");
p.save();
Logger.debug("Complex calculation: " + p.getNameLength());
yields then:
[debug] application - Complex calculation: 6
[debug] application - Complex calculation: 16
What's wrong about property in the model? Just make it private add the public getter but without setter, finally override save and update methods.
private Integer nameLength;
// BTW shouldn't you also count the space between first and last name?
public Integer getNameLength() {
return firstName.length() + lastName.length();
}
#Override
public void save() {
nameLength = firstName.length() + lastName.length();
super.save();
}
#Override
public void update() {
nameLength = firstName.length() + lastName.length();
super.update();
}
If you still want to avoid property in the model, you will need to use custom SQL query (probably also within the overridden save/update methods), like showed in other answer or Ebean's docs, note that will perform at least 2 SQL queries per each save or update operation.
Thanks to Anton and biesior for giving me some ideas.
Rather than override the save or update methods, we opted for a private variable that is recalculated when the variables it's dependent on are updated.
public class Person {
#Id
public Long id;
private String firstName;
private String lastName;
private Integer nameLength;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void setFirstName() {
this.firstName = firstName;
calculateNameLength();
}
public void setLastName(String lastName) {
this.lastName = lastName;
calculateNameLength();
}
private void calculateNameLength() {
nameLength = getFirstName().length + getLastName().length;
}
}
This has several benefits over the suggested methods of updating the value in the save or update methods.
Recalculating in the save or update method means we need to call one of those methods every time we set a field on the object. If we don't, the nameLength will get out of sync. I couldn't, for example, change the Persons firstName, and then use the nameLength to do something else without first persisting the object to the database.
Furthermore, using the save/update methods couples the objects state to the Ebean. The ORM is for persisting object state, not for setting object state.
As the title says, there has always been quite a discussion about getters and setters in any programming language, so also Java.
The question is the following: Are there any new arguments since Java 8 got released?
An example of an already existing argument is that getters and setters encapsulate state, or that they make it possible to change the implementation without changing the API.
Yes, there are! Since Java 8 method references were introduced, and as their name says, they can only be used with methods.
Consider the following code:
class Person {
private String firstName;
private String lastName;
public Person(final String firstName, final String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Override
public String toString() {
return firstName + " " + lastName;
}
}
Assume we want to obtain a map that contains a lists of people grouped by their last name, we can only do that via method references with the following code:
List<Person> personList = new ArrayList<>();
personList.add(new Person("Shannon", "Goldstein"));
personList.add(new Person("Donnie", "Denney"));
personList.add(new Person("Mark", "Thomas"));
personList.add(new Person("Julia", "Thomas"));
Map<String, List<Person>> personMapping = personList.stream()
.collect(Collectors.groupingBy(Person::getLastName));
System.out.println("personMapping = " + personMapping);
Which prints out, formatted nicely:
personMapping = {
Thomas=[Mark Thomas, Julia Thomas],
Goldstein=[Shannon Goldstein],
Denney=[Donnie Denney]
}
This would not have worked if we were using public variables, as you cannot obtain a method reference on them, nor reference them in another way other than writing a full-fledged lambda where it is not neccessary.
(For curious people: person -> person.lastName would need to have been used)
Also, keep in mind that this answer differs from someone claiming that if an object needs to adhere to a certain interface, that then getters and setters must be used. As in this example the Person class adheres to no interface, yet benefits from having getters available.
public class Customer {
private String email;
public void setEmail(String email) { this.email = email; }
public String getEmail() { return email; }
}