Serializing JPA Entity - saving only IDs for fetched entities - java

There are two entities: User and Employee. User has field with type Employee.
#Entity
#Table(name="user")
public class User extends AuditableEntity {
Long idUser;
String username;
String password;
Employee employee;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getIdUser() { return idUser; }
public void setIdUser(Long idUser) { this.idUser = idUser; }
#Column(name = "username")
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
#Column(name = "password")
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
#ManyToOne
#JoinColumn(name = "idemployee")
public Employee getEmployee() { return employee; }
public void setEmployee(Employee employee) { this.employee = employee; }
}
And
#Entity
#Table(name = "employee")
public class Employee extends AuditableEntity {
Long idEmployee;
String surname;
String name;
String patronymic;
Date birthdate;
#Id
#GeneratedValue (strategy=GenerationType.IDENTITY)
#Column(name = "idemployee")
public Long getIdEmployee() { return idEmployee; }
public void setIdEmployee(Long idEmployee) { this.idEmployee = idEmployee; }
#Column(name = "surname")
public String getSurname() { return surname; }
public void setSurname(String surname) { this.surname = surname; }
#Column(name = "name")
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#Column(name = "patronymic")
public String getPatronymic() { return patronymic; }
public void setPatronymic(String patronymic) { this.patronymic = patronymic; }
#JsonFormat(pattern = "dd.MM.yyyy")
#Column(name = "birthday")
public Date getBirthdate() { return birthdate; }
public void setBirthdate(Date birthdate) { this.birthdate = birthdate; }
}
I need to serialize User to XML/JSON. I'm using JAXB but it's serializing Employee too:
<User>
<idUser>15</idUser>
<username>user15</username>
<password>password15</password>
<employee>
<idEmployee>23</idEmployee>
<surname>Smith</surname>
<name>John</name>
<patronymic>H.</patronymic>
<birthdate>01.01.1970</birthdate>
</employee>
<User>
I need in result something like this:
<User>
<idUser>15</idUser>
<username>user15</username>
<password>password15</password>
<idEmployee>23</idEmployee>
<User>
I tried to use #XmlID, #XmlIDREF - but it's usable only with String id column.
Also tried to use #XmlTransient - but it's only excluding Employee.
How i can serialize User without Employee, only with idEmployee?
And second question is deserialization. Are there any standard ways to do that?

Annotate property employee in class User with #XmlTransient. This annotation can be applied to either field or its getter.

You can add additional getter for Employee's id to User entity
public class User
{
...
#Transient // for JPA
#XmlElement
Long getIdEmployee()
{
return employee.getIdEmployee();
}
}
Or you can do it with #XmlValue
public class User
{
...
#XmlElement(name = "idEmployee")
public Employee getEmployee() { return employee; }
public void setEmployee(Employee employee) { this.employee = employee; }
}
#XmlAccessorType(XmlAccessType.NONE) // to prevent marshalling of all properies
public class Employee
{
...
#XmlValue
public Long getIdEmployee() { return idEmployee; }
...
}

Related

no setter methods in generated mapstruct implementation

I'm experimenting with mapstruct and follow this tutorial:
mapstruct tut
I have this entity:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_company")
#SequenceGenerator(name = "seq_company", allocationSize = 1)
#Column(nullable = false, updatable = false)
private Long id;
private String name;
private String shortName;
public Company() {
}
public Company(Long id, String name, String shortName) {
this.id = id;
this.name = name;
this.shortName = shortName;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
}
And this is the simple dto:
public class CompanyDto {
#JsonProperty("id")
private Long id;
#JsonProperty("name")
private String name;
#JsonProperty("shortName")
private String shortName;
}
And here is the mapper interface:
#Mapper(componentModel = "spring")
public interface CompanyMapper {
CompanyDto companyToCompanyDto(Company company);
Company companyDtoToCompany(CompanyDto companyDto);
List<CompanyDto> companiesToCompanyDtos(List<Company> companies);
}
I certanly oversee something, because there is no setters in the generated implementation, f. e.:
#Override
public Company companyDtoToCompany(CompanyDto companyDto) {
if ( companyDto == null ) {
return null;
}
Company company = new Company();
return company;
}
What goes here wrong?
I've noticed that your CompanyDto class has private fields but no getters or setters. There is no standard way to access the fields in that class. You might need to add those in order to map in or out of that class.

JPA mapping table could not delete

I have entity Account, Role, AccountRole.
#Entity
public class Account {
#Id
private String loingId;
private String username;
private String password;
private String email;
private boolean enable;
#OneToMany(mappedBy = "account", orphanRemoval = true)
private List<AccountRole> accountRoles = new ArrayList<>();
public String getLoingId() {
return loingId;
}
public void setLoingId(String loingId) {
this.loingId = loingId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public List<AccountRole> getAccountRoles() {
return accountRoles;
}
public void setAccountRoles(List<AccountRole> accountRoles) {
this.accountRoles = accountRoles;
}
public void addAccountRoles(AccountRole accountRoles) {
if (this.accountRoles == null){
this.accountRoles = new ArrayList<>();
}
this.accountRoles.add(accountRoles);
accountRoles.setAccount(this);
}
public void removeAccountRoles(){
this.accountRoles = null;
}
}
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
private boolean enable;
#OneToMany(mappedBy = "role")
private List<AccountRole> accountRoles = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public List<AccountRole> getAccountRoles() {
return accountRoles;
}
public void setAccountRoles(List<AccountRole> accountRoles) {
this.accountRoles = accountRoles;
}
}
#Entity
public class AccountRole implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "account_id")
private Account account;
#ManyToOne
#JoinColumn(name = "role_id")
private Role role;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
To create account with role is OK.
There is a problem in update.
I want to delete the existing Role and only add the changed Role when the Role of the Account is changed. However, existing data is not deleted from the AccoutRole table.
How can I solve the problem?
springBootVersion = '1.5.3.RELEASE'
java 1.8
gradle dependencies
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
testCompile('org.springframework.boot:spring-boot-starter-test')
runtime ('org.mariadb.jdbc:mariadb-java-client')
}
A couple of ideas:
Thought 1: Try using cascade
Yes, JPA 2.0 should handle this with orphanRemoval = true, but let's just see if that works. I think that it is not because you aren't creating an orphan here. The mapping is still "valid" from a relational perspective.
#OneToMany(mappedBy = "account", cascade = CascadeType.ALL) // or CascadeType.REMOVE
private List<AccountRole> accountRoles = new ArrayList<>();
Thought 2: Try setting the account roles to an empty hashmap instead first:
account.setAccountRoles(new HashMap<AccountRole>());
account.getAccountRoles().add(accountRole);;

Declaring class is not found in the inheritance state hierarchy: UserAccount

I'm using servlets with JPA+Hibernate). I don't understand the error, unless I've tried other solutions suggested in this forum. In fact, I don't want to store the UserAccount class as an entity; but I just want to declare it in the Utilisateur class (the Ids of the Utilisateur class are declared in the useraccount class).
My code :
#Entity
#Table(name = "utilisateur")
public class Utilisateur implements Serializable {
#Id
private UserAccount userAccount;
private Civility civility;
private Address address;
private Contact contact;
#Column(name = "sessions")
private List<String> sessions;
#Column(name = "particularRules")
private boolean particularRules;
public Utilisateur(UserAccount pAccount, Civility pCivility,
Address pAddress, Contact pContact, List<String>
pSessions,
boolean particularRules) {
this.userAccount = pAccount;
this.civility = pCivility;
this.address = pAddress;
this.contact = pContact;
this.sessions = pSessions;
this.particularRules = particularRules;
}
public Civility getCivility() {
return civility;
}
public void setCivility(Civility civility) {
this.civility = civility;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public boolean isParticularRules() {
return particularRules;
}
public void setParticularRules(boolean particularRules) {
this.particularRules = particularRules;
}
public UserAccount getUserAccount() {
return userAccount;
}
public void setUserAccount(UserAccount userAccount) {
this.userAccount = userAccount;
}
public List<String> getSessions() {
return sessions;
}
public void setSessions(List<String> sessions) {
this.sessions = sessions;
}
}
#Embeddable
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class UserAccount implements Serializable {
public UserAccount() {
}
public UserAccount(String pId, String pEmail, String pwsd, Date pCreaDate, Date pLastModDate) {
this.identifier = pId;
this.email = pEmail;
this.password = pwsd;
this.creationDate = pCreaDate;
this.lastModificationDate = pLastModDate;
}
#OneToOne(mappedBy = "userAccount", cascade = CascadeType.ALL,
fetch = FetchType.EAGER, orphanRemoval = true, targetEntity =
Utilisateur.class)
private Utilisateur user;
#Column(name = "creationDate")
#Temporal(javax.persistence.TemporalType.DATE)
private Date creationDate;
#Column(name = "lastModificationDate")
#Temporal(javax.persistence.TemporalType.DATE)
private Date lastModificationDate;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "identifier", nullable = false)
private String identifier;
#Column(name = "email")
private String email;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "password", nullable = false)
private String password;
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public Date getLastModificationDate() {
return lastModificationDate;
}
public void setLastModificationDate(Date lastModificationDate) {
this.lastModificationDate = lastModificationDate;
}
public Utilisateur getUser() {
return user;
}
public void setUser(Utilisateur user) {
this.user = user;
}
}
You must use an Embedded Primary Key.
See the answer to this question here in Stackoverflow How to create and handle composite primary key in JPA.
Best regards!
It may occur when Embedded Primary Key contains #EmbeddedId. Sub-composite key should be annotated with #Embedded instead.

How to make a join on Java with criteria builder

I have a database in which I need to make a Join from Java with a CriteriaBuilder.
I have this code so far:
CriteriaBuilder cb = entman.getCriteriaBuilder();
CriteriaQuery<Company> query = cb.createQuery(Company.class);
Root<Employee> teacher = query.from(Employee.class);
Join<Employee, Company> employees = teacher.join("id");
query.select(employees).where(cb.equal(teacher.get("name"), ""));
List<Company> results = entman.createQuery(query).getResultList();
return results;
After I run this code ( with springboot) i get this error: Cannot join to attribute of basic type
Does anyone know what should I do to make it work ?
PS: I will provide any other information if needed.
Thanks in advance.
company database
employee database
L.E.:
Employee:
#Entity
public class Employee {
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
#Size(min = 1)
private String name;
#Column(nullable = false)
#Temporal(TemporalType.DATE)
private Date hire_date;
#ManyToOne
//#JoinColumn(name = "id")
private Company company;
public Employee() {}
public Employee(Long id, String name, Date date, Company company) {
setId(id);
setName(name);
setHire_date(date);
setCompany(company);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getHire_date() {
return hire_date;
}
public void setHire_date(Date hire_date) {
this.hire_date = hire_date;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
Company:
#Entity
public class Company {
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
#Size(min = 1)
private String name;
#OneToMany(mappedBy="company", cascade = CascadeType.ALL, fetch=FetchType.EAGER, orphanRemoval=true)
private Collection<Employee> employees;
public Company() {}
public Company(Long id, String name) {
setId(id);
setName(name);
}
public Company(Long id, String name, Collection<Employee> employees) {
setId(id);
setName(name);
setEmployees(employees);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<Employee> getEmployees()
{
return employees;
}
public void setEmployees(Collection<Employee> employees) {
this.employees = employees;
}
#Override
public String toString() {
return "Company [id=" + id + ", name=" + name + ", employees=" + employees.toString() + "]";
}
}
You need to have a Teacher element, not a reference to id because you cannot join a #Column field...
Change id mapping from #Column:
#Column(name = "id")
private Integer id;
with a #ManyToOne (or the needed one) association:
#ManyToOne
#JoinColumn(name = "id")
private Teacher teacher;
After this your join will work as expected.

Spring boot OnetoMany with JPA

I have started testing with spring boot to create a Restful webservice that has
simple crud functions.i have two entity classes
Company.java
#Entity
#Table(name="Company_new")
public class Company {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public void setId(int id) {
this.id = id;
}
#NotNull
#Column(name="name")
private String name;
#OneToMany(mappedBy="company",cascade=CascadeType.ALL)
private Set<User> users;
public Company(String name){
this.name = name;
}
public Company(){
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public int getId(){
return id;
}
}
and User.java
#Entity
#Table(name="user_new")
public class User {
#Id
#Column
#GeneratedValue(strategy = GenerationType.AUTO)
private int idUser;
#NotNull
#Column
private String name;
#NotNull
#Column
private String userName;
#NotNull
#Column
private String authLevel;
#Column
private String password;
#ManyToOne
#JoinColumn(name="idCompany")
private Company company;
// Public methods
public Company getCompany(){
return company;
}
public void setCompany(Company company){
this.company = company;
}
public void setName(String name){
this.name =name;
}
public void setUserName(String userName){
this.userName = userName;
}
public String getName(){
return this.name;
}
public String getUsername(){
return this.userName;
}
public String getPassword(){
return this.password;
}
public String getAuthLevel() {
return authLevel;
}
public void setAuthLevel(String authLevel) {
this.authLevel = authLevel;
}
public String getUserName() {
return userName;
}
public void setId(int idUser) {
this.idUser = idUser;
}
public void setPassword(String password) {
this.password = password;
}
public int getId(){
return this.idUser;
}
}
i want to have a relationship with a Company having many users.
I have tried presisting a user record like this
#Autowired
CompanyDao companyDao;
#RequestMapping(value = "/user/create", method = RequestMethod.POST)
public #ResponseBody ResponseEntity createUser(#RequestBody User user) {
try {
Company c = companyDao.findOne(user.getCompany().getId());
user.setCompany(c);
userDao.save(user);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(user, HttpStatus.OK);
}
my data is presisting in the database the way i want
but when i try to access a company record it loads like this
obviously it loads relationships in a cycle and eventually gives stack overflow error. how to solve this ?
Depending on your desired outcome you can:
use #JsonIgnore on public Set<User> getUsers() to prevent serializing the users collection or #JsonIgnore on public Company getCompany() to prevent company serialization
use #JsonManagedReference on public Set<User> getUsers() and #JsonBackReference on public Company getCompany() to let Jackson know that it's a bidirectional relation.
PS. If the API you're exposing will be consumed by code you do not control consider not exposing entities directly but mapping them to DTOs

Categories