How in JPA mappe into one column values of two other columns? - java

I am using JPA, and in my DB I have a table student with columns: id, firstname and firstAndLastname
So I did an Entity to mappe data, I can get lastname from another table, but I don't persist it as there is no columns lastname in DB.
How to store in column firstAndLastname that is concatenation of firstname and lastname and store it in specific column, example : firstname : John, lastname : Doe => firstAndLastname : JohnDoe
#Entity
#Table(name = "student")
public class Student implements Serializable {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "firstname")
private String firstname;
#Transient
private String lastname;
#Column(name = "firstAndLastname")
private String firstLastname;
I tried this (and not working) :
public String getFirstLastname() {
return this.firstname+ this.lastname;
}
Any idea ?

Genetate getters/setter for all properties of the Entity.
Instantiate the object to save: Student student = new Student(1,"Mario","Rossi");
Call the setter : student.setFirstLastName(student.getFirstName()+student.getLastName());
myStudentService.save(student);.

If you still haven't figured out how to persist data to the database, that's where you need to start. You don't persist data in the JPA itself, that's just the structure for the object. You will build an instance of the Student object in another Class, set the values, and persist it to the database.
You would set that firstLastName value where/when you are creating an instance of your Student.
This is just a rough explanation. Not explicit code for you.
You would have a class like:
public class SaveStudentService {
public saveStudent(){
Student student = new Student();
student.setFirstName("test");
student.setLastName("lastTest");
student.setFirstLast(student.getFirstName + " "+ student.getLastName);
studentRepo.save(student);
}
}
You'll have your normal getters and setters in your Student class...
public Student getfirstName() {return this.firstName;}
public Student setfirstName(String firstName) {return this.firstName = firstName;}
public Student getfirstLast() {return this.firstLastName;}
public Student setfirstName(String firstLastName) {return this.firstLastName = firstLastName;}

I would recommend 2 approaches:
If you instantiate object by constructor:
public Student(final Integer id, final String firstname, final String lastname) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.firstLastname = String.join(firstname, lastname);
}
By setter:
public void setFirstLastname(final String firstName, final String lastName) {
this.firstLastname = String.join(firstName, lastName);
}
In addition you can also use Spring AOP and advice save method on your repository but it's less readable and more difficult to maintenance.
#Before("execution(* com.package.StudentRepository.save(..))")
public void updateFirstLastName(final JoinPoint joinPoint) {
final Object[] arguments = joinPoint.getArgs();
// find Student.class object and update firstLastName field
}
Either way - don't perform concatenation outside Student class because it leads to boilerplate and it's harder to maintenance.
But IMHO the best approach is to store firstName and lastName separately in database and use SQL CONCAT function if needed.

Related

trying to call superclass method in subclass

Probably a pretty noob question, but I cant figure it out. I have a class Person to store a name that is input from the keyboard
public class Person {
private String firstName;
private String lastName;
public Person()
{
firstName = "";
lastName = "";
}
public Person(String first, String last)
{
setName(first, last);
}
public String toString()
{
return(firstName + " " + lastName);
}
public void setName(String first, String last)
{
firstName = first;
lastName = last;
}
public String getFirstName()
{
return firstName;
}
public String getLastName()
{
return lastName;
}
}
I am trying to call the toString method in a subclass called Patient
public class Patient extends Person {
private int patientID, patientAge;
public Patient()
{
patientID = 0; //for a different part of the class that works
patientAge = 0;
}
#Override
public String toString()
{
return ("Patient Name: "+super.toString());
}
}
I cannot get it to output the name in main when I call the toString method from the Patient class, but when I tested it, it output the name when I call the toString method from the Person class.
The method call in main looks like Patient pnt = new Patient(); System.out.print(Pnt.toString());
it prints out in the console "Patient Name: ". Any feedback on what I am doing wrong or ideas on how to get it to work
Here:
public Person()
{
firstName = "";
lastName = "";
}
Your subclass is missing a reasonable call to a super class constructor. So when you instantiate your Patient objects, the above constructor is used, and all patients end up with "" as first and last name!
When you create a Patient, then a patient should have a name, too! But your constructor in Patient only sets the Patient related fields. And implicitly, the default super constructor is called. Therefore the Person fields are all set to be empty strings!
A much better approach would look like this:
class Person {
private final String firstName;
... lastName
public Person(String firstName, String lastName) {
this.firstName = firstName;
...
and then
class Patient extends Person {
private final int patientID;
public Patient(int patientID, String firstName, String lastName) {
super(firstName, lastName);
this.patientID = patientID;
)
Why is that better: names and IDs don't change (normally). There is no point in having getters for them. You create your object once, and then that data is fixed! There is also no point in having that default constructor in Person. A person with empty names doesn't make sense. Thus: don't create a class that allows you to create "invalid" objects. Your classes model reality. There are no real people without names!
And one other hint: use #Override when overriding methods, so that the compiler can tell you when you get something wrong!
if the problem is to output the name in main when you call the toString method from the Patient class, I think the code bellow will help you.
have you tried to construct the Patient object like this?
public static void main(String[] args) {
Patient p = new Patient();
System.out.println(p.toString());
}
Actually I do not see problem in your code.
Person person = new Person();
person.setName("aa", "bb");
System.out.println(person); // aa bb
Patient patient = new Patient();
patient.setName("cc", "dd");
System.out.println(patient); // Patient Name: cc dd
I think that you set name wrong pr use not correct reference. Check it.
You don't have any constructor for your PATIENT subclass. You don't set any firstName or lastName to any patient.
To keep familiar constructor as you used in your parent class, tru to use:
public Patient() {
super("default_firstName", "default_lastName");
this.patientID = 0;
this.patientAge = 0;
}
public Patient(String firstName, String lastName, int patientAge) {
super(firstName, lastName);
this.patientID = 0; //can be implemented some method for automatically setting numbers
this.patientAge = patientAge;
}
This way you always get firstName and lastName even if constructor will be called empty.
According to you toString method, it's correct and it call super class method:
#Override
public String toString()
{
return("Patient name is "+super.toString());
}
But notice that you return STRING value so to make it visible on the screen remember to use:
System.out.println(patient.toString());
Then it will be visible :)
I have added some comments and code in your Person class that should fix your issues.
public class Person {
private String firstName; //store the first name
private String lastName; //sore the last name
//initialize firstName and lastName to an empty string
public Person() {
firstName = "";
lastName = "";
}
//set firstname and lastname according to the parameters.
public Person(String first, String last) {
//setName(first, last); remove this crap.
// Use the contructor properly when initialize your person object. Like this:
this.firstName = first;
this.lastName = last;
}
//method to output the first name and last name
#Override
public String toString() {
return (firstName + " " + lastName);
}
//method to set firstName and lastName according to the paramters
public void setName(String first, String last) {
//
firstName = first;
lastName = last;
}
}

ReST resource/model with optional fields

I am trying to learn how to implement a CRUD ReST API. I created a simple application using JAX-RS and am testing my HTTP methods using Postman. My question is, how do I define optional fields for a model for POST method?
ex. Person contains firstName, lastName as required fields, and age, gender as optional fields.
#XmlRootElement
public class Person {
private long id;
private String firstName;
private String lastName;
public Person() {}
public Person(long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
//getter and setters omitted
Above is a sub-resource to another resource, and below addPerson service method is used to POST to a HashMap.
public Person addPerson(long userId, Person person) {
Map<Long, Person> persons = users.get(userId).getPersons();
person.setId(persons.size() + 1);
persons.put(person.getId(), person);
return person;
}
I thought of constructing a model whose constructor has multiple different combination of parameters to instantiate a model class, but that doesn't seem very efficient. Can somebody advise?
FURTHER EDIT: This needs to be done while processing JSON. Given two JSON formatted request:
{
"firstName": "Jay",
"lastName": "Kay",
"age": "13"
}
and
{
"firstName": "Jay",
"lastName": "Kay"
}
both should be processable since age(as well as gender) is an "optional attributes" so it may be either omitted, or have a value. I'm coming across JSON parsers and trying to read through them since that may be an answer to processing such requests.
Add gender and age to your Person class
#XmlRootElement
public class Person {
private long id;
private String firstName;
private String lastName;
private int age;
private String gender;
public Person() {}
public Person(long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
}
While making request from Postman, include the age and gender as keys in x-www-form-urlencoded.
By default, the value of integer is 0 and value for String is empty string.
In your method to handle the request, check the values for gender and age.
If the values are provided in the request, then use the setters of Person class to set the values accordingly.
Suppose the following is your method to handle the request :
#POST
#Path("/AddPerson")
#Produces(MediaType.TEXT_PLAIN)
public String addPerson(#FormParam("id") long id,
#FormParam("firstName") String firstName,
#FormParam("lastName") String lastName,
#FormParam("age") int age,
#FormParam("gender") String gender) {
Person person = new Person(id,firstName,lastName);
if(age != 0) {
person.setAge(age);
}
if(!gender.equals("")) {
person.setGender(gender)
}
// Assuming that PersonService class has the addPerson method
PersonService.addPerson(1,person)
return "Person with id "+ id + " added";
}

Unable to display object array using get(index)

I am trying to display the different objects in an ArrayList. In my project context, one student is one object.
I used an ArrayList to store all the different student objects and I am having problems reading the ArrayList.
<%
String student_name = request.getParameter("studentName");
ArrayList<Object[]> studentList = new ArrayList<Object[]>();
if(student_name != null && student_name.length() > 0) {
PreparedStatement preparedStatement = con.prepareStatement("Select * from users where firstname LIKE ? ");
preparedStatement.setString(1, "%" +student_name+ "%");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
String first_name = resultSet.getString("firstname");
String last_name = resultSet.getString("lastname");
String email = resultSet.getString("email");
Object[] student = {first_name,last_name,email};
studentList.add(student);
session.setAttribute("studentObject",studentList);
//System.out.println("First Name: " + first_name + "," + "Last Name: " + last_name);
System.out.println(studentList.get(0));
}
When I try to display (studentList.get(0)), all I see is "[Ljava.lang.String;#XXXX"
How do i get it to display the different student objects based on the index ?
At first, It will be more idiomatic in Java to define your own class Student. Write an extractor to that class, define toString method and it will be great.
Java requires you to define toString method to any type of object that will be printed. So, you have to define toString for your Student class.
But you are using an array. Java doesn't have toString method defined for Arrays. So I would propose you to do something like this. (I'm a bit on rush so code may contain some mistakes:
// At first it could be better to define a structure
// that represents a student
// class must be defined in separate file like Student.java
// Student.java
public class Student {
// constructor for the Student object
public Student(final String firstName,
final String lastName,
final String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
private String firstName;
private String lastName;
private String email;
#override String toString() {
return firstName + " " + lastName + " " + email;
}
// getters
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public String getEmail() { return email; }
// setters
public void setFirstName(final String firstName) {
this.firstName = firstName;
}
public void setLastName(final String lastName) {
this.lastName = lastName;
}
public void setEmail(final String email) {
this.email = email;
}
} // end of Student.java file
///////////////////////////////////////////
final ArrayList<Student> studentList = new ArrayList<Student>();
.... // your code
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
// creating a new student with new keyword
final Student currentStudent = new Student(
resultSet.getString("firstname"),
resultSet.getString("lastname"),
resultSet.getString("email")
)
// you are adding a student object to the list
studentList.add(currentStudent);
session.setAttribute("studentObject",studentList);
// that should work
// toString method will be called implicitly
System.out.println(studentList.get(0));
}
// So you will have a list of students
// that will be accessable in the following manner:
studentList.get(0).getFirstName;
studentList.get(1).setFirstName("New name");
If you want a different behaviour you may call those fields directly, or modify
the behavior of toString method
Java also assumes that you're using camelCase notation, you may find in in the style guide.
Hope it helps.
Try java.util.Arrays class:
System.out.println(Arrays.toString(studentList.get(0)));
Currently you are printing out the Object[] which is not exactly human readable. I would suggest creating a new class called Student. Then have three member variables firstName, lastName, and email. Also create a toString() method for this new class. Then you can have an ArrayList of Students. This should simplify everything for you.
You have at least two options:
Implement an object corresponding to Object[] student:
In this class you may override Object's toString(), and implement it as to wish to present the data.
NOTE: this is very relevant to you because when printing studentList.get(0) you actually print call Object's default toString() which returns the reference to the object.
For more information regarding default Object.toString() here.
The simplestway is to use Arrays.toString():
System.out.println(Arrays.toString(studentList.get(0)));
It looks like you're not trying to display a String as you expect, you're trying to display an array of Objects.
You could try : System.out.println(studentList.get(0)[0]);
You'll have an idea

Ebean: How to create a column for a getter without a property

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.

How to instantiante object & call setter on same line?

If I have an Employee class with a default constructor:
private String firstName;
public Employee(){}
and a setter:
public void setFirstName(String firstName){
this.firstName = firstName;
}
Why does this attempt fail to instantiate and call the setter in the same line?
Employee employee = new Employee().setFirstName("John");
You can also use this syntax:
Employee employee = new Employee() {{
setFirstName("John");
}};
Though keep in mind that it's going to create an anonymous inner class and probably isn't what you want.
Because setFirstName doesn't return anything. If you want to chain methods then setFirstName would have to return Employee.
Another approach is to have a constructor that takes firstName as an argument.
(employee = new Employee()).setFirstName("John");
performs instantiation and calling the setter, as you requested in the headline, but does not declare the variable as suggested in your code example.
(Employee employee = new Employee()).setFirstName("John");
will probably not work, I assume. But you can try.
Of course, you can always stuff multiple statements in one line.
Employee employee; (employee = new Employee()).setFirstName("John");
or
Employee employee = new Employee(); employee.setFirstName("John");
If I were you, I would settle for a parameterized constructor, though.
The method serFirstName is of return type void (nothing). Try:
public Employee setFirstName(String fname) {
this.firstName = fname;
return this;
}
It should be like this:
Employee employee = new Employee();
employee.setFirstName("John");
Although this is a bit overkill, you could try using the builder pattern
public class Employee{
private String firstName;
public static class Builder{
private String firstName;
public Builder firstName(String firstName){
this.firstName = firstName;
return this;
}
public Employee build(){
return new Employee(this);
}
}
private Employee(Builder builder){
firstName = builder.firstName;
}
}
Then you can do the following
Employee e = new Employee.Builder().firstName("John").build();
Because the you want to set employee to the value of .setFirstName("John"); which does not return anything because it is a void
So you could either change your setter to:
public Employee setFirstName(String fname) {
this.firstName = fname;
return this;
}
OR Create a second constructor for Employee
public Employee(String fname){this.firstName = fname;}
Which would set firstname on init.
In order for your code to work, you would have to return the Employee (meaning "this") in the setter method setFirstName.
If you don't own the Employee class (I know this is just a simple example - but for the sake of argument) and cannot modify it, one way to solve that is using functional programming. You could declare yourself a function like this:
static final Function<String, Employee> EMPLOYEE = firstName -> {
Employee employee = new Employee();
employee.setFirstName(firstName);
return employee;
};
And then you can create your employee in one line like this:
Employee jake = EMPLOYEE.apply("Jake");
Maybe not exactly what you want, but still useful.

Categories