In JPA mapping class to primitive - java

I want to mapping a class to a primitive type
#Entity
#Table(name="PERSON")
public class Person implements Serializable {
...
private Email email = null;
...
#Column(name="email")
public Email getEmail() {
return email;
}
public void setEmail(Email email) {
this.email=email;
}
}
In this example I want to save the email like a String
I wish something like:
interface someInterface{
String getValue();
void setValue(String p);
}
Then if I implement this interface in Email it can be save and loaded transparently (I haven't find something like this).
I don't want to serialize the class
I use Email as a example, but I have in mind many other class, to little to be a whole entity.
Any idea?

JPA2.1 provides AttributeConverter which means that a field can be marked with a converter persisting it as, for example, a String. All JPA implementations have their own variant currently, but DataNucleus JPA provides the JPA2.1 feature already, see the foot of this page.

Make two variables. Email which is object and that you mark as #Transient and emailStr that you store in the database. The emailStr getter and setter should be private, and the getEmail creates the email object (lazily) based on the emailStr.
Another solution, if you have email in many entities is to create a custom UserType.
Just google hibernate UserType..

You can use converters in EclipseLink for this,
see,
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Basic_Mappings/Default_Conversions_and_Converters

There's nothing stopping you from using String in your Classes as far as JPA is concerned. Or any other primitive type for that matter...
EDIT: You mean something like this hopefully
#Entity
#Table(name="PERSON")
public class Person implements Serializable {
...
private String email = null;
...
#Column(name="email")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email=email;
}
}

Related

Java - Convert classes

I have 2 classes:
public class Customer {
private String firstname;
private String lastname;
}
and
public class Buyer {
private String firstname;
private String lastname;
}
I want both classes to convert to a the following class:
public class CustomerDTO {
private String firstname;
private String lastname;
}
I cannot use a common interface for the classes or something. Is there a way I can convert Customer and Buyer both to a Customer DTO class with a single converter class?
You can't convert (map) two classes (Customer, Buyer) to a third one (CustomerDTO) with a single converter class because there is no common type (i.e. an interface) shared by your two classes. Java does not use duck typing so you cannot rely to the fact that your classes look similar. Compiler does not see that.
Having that said, you can at least automate the generation of the mappers you need. An option would be MapStruct. In your case, mapping would be straightforward (based on documentation):
#Mapper
public interface CustomerMapper {
CustomerDto toCustomerDto(Customer customer);
CustomerDto toCustomerDto(Buyer buyer);
}
MapStruct is a compile time dependency: it will generate the mappers for you at compile time, so you can review them.
Thanks Royal Bg for the suggestion to use method overloading (toCustomerDto) for a much cleaner solution.
You can use model mapper, for example checkout this http://modelmapper.org/getting-started/
this is a sample code you can add this to your DTO code for both Customer and Buyer
public UserDTO(Cutomer user) {
this.id = user.getId();
this.login = user.getLogin();
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
this.email = user.getEmail();
this.authorities = user.getAuthorities().stream()
.map(Authority::getName)
.collect(Collectors.toSet());
}

How to have a custom type behave as a String with QueryDSL

I am trying to use a custom type with QueryDSL, something like this:
public class Email implements Comparable<Email> {
public String name;
public String domain;
#Override
public int compareTo(Email o) {
return toString().compareTo(o.toString());
}
#Override
public String toString() {
return name + "#" + domain;
}
}
I then have a QueryDSL entity that uses this custom type:
#Entity
public class Record {
public Email email;
public String text;
}
I can then build Queries like this:
BooleanExpression withEmail(Email email)
{
QRecord Q = QRecord.record;
BooleanExpression pred = Q.email.eq(email);
return pred;
}
However, when using MongodbSerializer, the query ends up setting the Email type in a mongo DBObject, and later I get an exception from the mongo driver saying that Email cannot be serialized.
java.lang.RuntimeException: json can't serialize type : class Email
If I annotated by email field in my Record like this:
#Entity
public class Record {
#QueryType(PropertyType.STRING)
public Email email;
public String text;
}
Then it starts working - although I have to manipulate the email with a email as strings in my query constructions instead of the email objects themselves, ex. QRecord.record.email.eq(myEmail.toString()) instead of simply QRecord.record.email.eq(myEmail) like before.
My issue with this solution is that anyone using the Email object has to know that they should annotate it when embedding it inside another object.
Question: is there a way to annotate the Email class in such a way that it will always be serialized as a string entity - or is there a better way to achieve this altogether?

why not hierarchy from entity to create dto?

Is there any reason to do not use hierarchy from an entity/model in order to create a dto/form object which help you to hold form search fields?
This is not a big system and these approach will help us to create real dto later if it is needed.
Our models are simple POJO's with almost any logic, maybe some validation logic but that would be valid also for the DTO.
I do not make sense to create a new DTO object with all the fields.
public class User {
private String name;
private String email;
private Date onboardingDate;
public User() {}
public User(String name, String email, Date onboardingDate) {
this.name = name;
this.email = email;
this.onboardingDate = onboardingDate;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Date getOnboardingDate() { return onboardingDate; }
public void setOnboardingDate(Date onboardingDate) { this.onboardingDate = onboardingDate; }
}
my DTO class, I can use it for use creation and for search purpose.
public class UserDTO extends User {
private Date fromDate;
private Date toDate;
public Date getFromDate() { return fromDate; }
public void setFromDate(Date fromDate) { this.fromDate = fromDate; }
public Date getToDate() { return toDate; }
public void setToDate(Date toDate) { this.toDate = toDate; }
public User convertToEntity() {
return new User(super.getName(), super.getName(), super.getOnboardingDate());
}
}
Thanks fox!
Usually, a DTO will be a subset of the entity data or also contain data from other associations in sub-DTOs or directly embedded in that DTO. If the DTO extends the entity, a user of a DTO object will have the possibility to invoke a getter to access all that state.
If your DTO is really a DTO, it will only have a subset of the data, but by extending from the entity, it might happen by accident that you access data that wasn't part of the subset that was loaded.
Imagine your user entity has detailed contact and address information. For one use case, you need that data, but for another you don't. It would not make sense to expose getters/setter for state that isn't there, would it? This is why one usually creates a separate DTO class for that purpose. You can still work with the entity type if you want to persist/update data, but even for these use cases, people sometimes tend to use DTOs because the persistent state does not necessarily represent the state which can be updated in a use case. This is especially important when you have state for e.g. denormalizations in your persistent state or cross cutting concerns like statistics or audit data.
If your model is so simple and will stay this way, then just use the entity model. If in 90% of the your use cases you need all data anyway, there is nothing you can gain from using DTOs.
Considering you have the need to create a subset of the entity state for your use cases I can only recommend you not to extend from the entity model and really just model what your use case requires. Never expose accessors to state that isn't there in DTOs. That will save you hours of debugging later.
Of course you could use your DTO for filter purposes, that's what is usually called filter by example, but you will notice that this has certain limits and quirks, so at some point you will need a different approach.
You can make use of a library that I develop called Blaze-Persistence Entity Views which allows you to create DTOs as interfaces. This is not only an easier way to model DTOs, but it will also perform better because it will only fetch the state really necessary for the desired representation.

JSR-303 Validation on sub-class

I have the following class structure
public Abstract class Person {
private String fullName;
private Address address;
private Phone ;
}
class Staff extends Person{
private String staffId;
}
I want to apply validation using JSR-303 on class Staff whereby Staff address and phone cannot have the value of null. However, I have some other classes that are class of Person where I don't wish to have the validation to be applied.
One way to do this that I could think of is by refactor Person and push the fields 'address' and 'phone' to Staff, but this means refactoring a lot of other classes (and not to mention redundancy this shall cause), and hence something I want to avoid.
Update.
I have changed Staff class, as follows
public class Staff extends Person {
#NotNull
private String staffEmploymentId;
public String getStaffEmploymentId() {
return staffEmploymentId;
}
public void setStaffEmploymentId(String id) {
this.staffEmploymentId = id;
}
#Override
#NotNull
public void setPhones(List<Phone> phones) {
super.phones = phones;
}
#Override
#NotNull
public void setAddress(Address a) {
super.address = a;
}
#Override
#NotNull
public Address getAddress(){
return super.address;
}
}
However, I've got the following error.
javax.validation.ValidationException: Property setAddress does not follow javabean conventions.
I am using Apache BVal, as opposed to Hibernate Validator.
Annotate getters instead of fields using annotations from JSR-330.
You can override getters in Stuff and annotate them.

What's the most object-oriented way to design an address book?

I am asking myself how to design an object-oriented address book in Java.
Let's say a contact can have several contact details, like addresses, phone numbers and e-mail addresses.
One way to implement this would be to give every contact an ArrayList for every type. But there must be a better and more object-oriented solution. What is it?
The most OOP suggestion I can give you is to create a class for every item/piece of information. For example:
public abstract class ContactInfo { /* ... */ }
public class Address extends ContactInfo { /* ... */ }
public class PhoneNumber extends ContactInfo { /* ... */ }
public class EmailAddress extends ContactInfo { /* ... */ }
public class Contact {
private String name;
private Set<ContactInfo> info;
// ...
}
and finally,
public class AddressBook {
List<Contact> contacts;
// ...
}
This may or may not be overkill for your specific case, but as a thought experiment, it's the way to go. It obviously takes care of the literal part of OOP — using objects — but also lays groundwork for encapsulation, abstraction and inheritance, which are closely related principles.
You're on the right track. The only thing I would do differently would be to use a List interface instead of an ArrayList collection to reference the contacts' attribute collections. This is advice based on the code-to-interfaces rule-of-thumb as described in this article and many others.
I don't think that's particularly un-object oriented. If your domain is such that a Person can have zero or more EmailAddresses, then you've almost exactly described the situation to use a list.
The only alternative approach I can think of would be to have fields such as
WorkEmail
PersonalEmail
OtherEmail1
OtherEmail2
OtherEmail3
but in my opinion that's worse, because:
You simply cannot support more than five email addresses (well, you could add more fields, but that increases the pain of the latter points and still imposes some finite limit.)
You're implying some extra semantics than may be present (what if the same address is used for work and personal? What if neither applies, can you just fill the Other ones? What if you don't know the purpose?)
You now have to test each field manually to see which is null, which is going to involve a non-trivial amount of duplication in Java. You can't use nice features like the enhanced-for loop to apply the same block to every email address, and you can't trivially count how many addresses there are
The list of properties that a Person has is now much less clean. I suppose you could package these properties into an EmailContactDetails class or something, but now you've got an extra level of indirection (more conceptual complexity) for no real gain.
So, if a person has a possibly-empty, unbounded list of email addresses, what's wrong with representing that as a list?
You can also use a Map, and then get specific values e.g. via myMap.get("emailAdress1") or iterate over the whole map like you would do with a list via myMap.entrySet().
One simple way to handle most of the use cases can be like this
public class AddressBook {
private Map<String, Contact> contacts;
AddressBook(){
contacts = new HashMap<String, Contact>();
}
public boolean addContact(Contact contact) {
if(contacts.containsKey(contact.getName())) {
System.out.println("Already exists");
return false;
}
contacts.put(contact.getName(), contact);
return true;
}
public boolean updateContact(Contact contact) {
contacts.put(contact.getName(), contact);
return true;
}
}
class Contact{
private String name;
private String email;
private String phone;
private Address address;
public Contact(String name) {
this.name = name;
}
public Contact(String name, String email, String phone, Address address) {
this.name = name;
this.email = email;
this.phone = phone;
this.address = address;
}
// getters and setters
#Override
public String toString() {
return "name is "+name+" and address is "+address;
}
}
class Address{
private String street1;
private String street2;
private String city;
private int zipcode;
public Address() {}
// getters and setters
#Override
public String toString() {
return "street1 is "+street1+" and zipcode is "+zipcode;
}
}

Categories