We have an Entity called Customers that has a OneToOne relationship to the Entity Address.
The Customer's PK should be manually defined. The Address' PK should be automatically defined.
So, in Customer I omitted the #GeneratedValue and I'm providing is value manually. But, when trying to save I'm getting the following error:
2018-11-07 10:42:17.810 ERROR 1257 --- [nio-8080-exec-2] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [Validation failed for classes [br.com.customers.entity.Address] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='não pode ser nulo', propertyPath=street, rootBeanClass=class br.com.customers.entity.Address, messageTemplate='{javax.validation.constraints.NotNull.message}'}
The problem is that the address.street is being provided and I can't realize why JPA is complaining that it's null...
Here are the JSON body that I'm trying to save. (It's being deserialized correctly, as, Address is not NULL)
{
"customer_Id": 50,
"name": "name",
"company_name": "company_name",
"email": "email#provider.com",
"business_phone": "(00) 1111-2222",
"mobile_phone": "(00) 1111-2222",
"document": "123456789",
"state_registration_number": "ISENTO",
"state_registration_type": "NO_CONTRIBUTOR",
"city_registration_number": "ISENTO",
"classification": "AUTO",
"address": {
"street": "STREET NAME",
"number": "NUMBER",
"complement": "COMPLEMENT",
"zip_code": "ZIP_CODE",
"neighborhood": "NEIGHBORHOOD",
"city": "CITY",
"state": "STATE"
}
}
Here are the Customer Entity:
#Data
#Entity(name = "X_CUSTOMERS")
public class Customer {
#Id
private int customer_Id;
#NotNull
private String name;
private String company_name;
private String email;
private String business_phone;
private String mobile_phone;
#NotNull
private String document;
private String state_registration_number;
private String state_registration_type;
private String city_registration_number;
#NotNull
private String classification;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinColumn(name = "address_id")
private Address address;
}
And here, Address Entity:
#Data
#Entity(name = "X_ADDRESS")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int address_Id;
#NotNull
private String street;
private String number;
private String complement;
private String zip_code;
private String neighborhood;
private String city;
private String state;
}
What Am I doing wrong?
Thanks!!!
Adding the code do persist the entities:
Customer Repository:
public interface CustomerRepository extends JpaRepository<Customer, Integer> {
}
To persist:
#RestController
#RequestMapping("/customers")
public class CustomersController {
private CustomerRepository customerRepository;
public CustomersController(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
#PostMapping
public Customer postCustomer(#RequestBody Customer customer) {
return customerRepository.save(customer);
}
}
From reading the Hibernate documentation, the save operation only persist entities with auto generated ids. So, if you intend to set the id yourself, then what you need, is to change your insert method for persist. And since you customer has an id that is not auto generated, maybe this could be the issue. You can read more in this blog.
#PostMapping
public Customer postCustomer(#RequestBody Customer customer) {
return customerRepository.persist(customer);
}
Hope it helps.
If you add CascadeType.MERGE, it will work
#OneToOne(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "address_id")
private Address address;
you set the customer id(50) so the following line of SimpleJpaRepository will be executed.
return this.em.merge(entity);
Related
I am building a Spring Rest Application, I need help with DTO's and parsing a result to a endpoint
This is json that I return at the moment to the endpoint:
{
"id": 1,
"name": "Ella - IPA Is Dead",
"description": "2015 IPA is Dead Series. Supremely floral, this hugely under-rated hop is related to Galaxy and was first cultivated in the Australian state of Victoria.",
"method": {
"mash_temp": [
{
"temp": {
"value": 65
}
}
]
}
}
I don't want to return "method" from this json, I just need "id", "name", "description", "mash_temp" - so it should look like this:
{
"id": 1,
"name": "Ella - IPA Is Dead",
"description": "2015 IPA is Dead Series. Supremely floral, this hugely under-rated hop is related to Galaxy and was first cultivated in the Australian state of Victoria. Initially given the same name as a certain Eurolager, their lawyers got involved and the St- prefix was dropped. Ella displays subtle notes of spice, but is fundamentally a truly floral bouquet, redolent of the Southern Hemisphere.",
"mash_temp": [
{
"temp": {
"value": 65
}
}
]
}
Those are the entities that I am using now:
Beer Entity:
#Entity
public class Beer implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "beer_id", unique = true, nullable = false)
private Integer id;
#Column(name = "name", nullable = false)
private String name;
#JsonProperty("description")
#Column(name = "description", nullable = false, columnDefinition = "TEXT")
private String description;
#JsonProperty("method")
#OneToOne(cascade = CascadeType.ALL)
private Method method;
}
Method Entity:
#Entity
public class Method implements Serializable
{
#JsonIgnore(value = true)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JsonProperty("mash_temp")
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "mash_temp")
private List<MashTemp> mash_temp = new ArrayList<>();
}
MashTemp Entity:
#Entity
public class MashTemp implements Serializable
{
#JsonIgnore(value = true)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne(cascade = CascadeType.ALL)
private Temp temp;
#ManyToOne
private Method method;
}
Temp Entity:
#Entity
public class Temp implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Integer value;
#JsonIgnore(value = true)
private String unit;
#OneToOne
private MashTemp mashTemp;
}
Does anyone know how to create DTO's from this Entities but without "method" field?
Also this is my Controller:
#GetMapping("/beers")
public ResponseEntity<Set<Beer>> getAllBeers()
{
return new ResponseEntity<>(beerService.getAllBeers(), HttpStatus.OK);
}
#GetMapping("/beers/{id}")
public ResponseEntity<Beer> getById(#PathVariable Integer id) {
Beer beer = beerService.findById(id);
return new ResponseEntity<>(beer, HttpStatus.OK);
}
Have a look at the #JsonUnwrapped annotation (https://fasterxml.github.io/jackson-annotations/javadoc/2.8/com/fasterxml/jackson/annotation/JsonUnwrapped.html). You can put it on the method field in the Beer class, and then the properties of the Method class are serialized directly on the same level as the ones from Beer.
I can not save Data. When I saved post request got error?
#Entity
#Table(name = "ALKP")
public class ALKP {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
#Column(name = "KEYWORD")
private String keyword;
private String code;
private Long slNo;
private String fullName;
private boolean isActive;
#CreationTimestamp
#Column(name = "created_at",updatable = false)
private LocalDate createDate;
#UpdateTimestamp
#Column(name = "updated_at")
private LocalDateTime updateDateTime;
#ManyToOne
#JoinColumn(name="parentId")
public ALKP parentId;
#OneToMany(mappedBy="parentId")
public Set<ALKP> subALKP = new HashSet<>();
Data ::
PostMan Body Request Data
{
"title": "FeMale",
"keyword": "GENDER_FEMALE",
"slNo": 2,
"active": true,
"code": "MC-00209",
"fullName": "FEMALE",
"parentId":700
}
"message": "detached entity passed to persist error in Spring Boot when consuming a rest service",
When I saved data it can not be catch Parent ALKP . I think It can be
parentId:{
"id":700
}
seems your ALKP enity have same parentId. or your defined the association mab be wrong.
#ManyToOne
#JoinColumn(name="parentId")
public ALKP parentId;
#OneToMany(mappedBy="parentId")
public Set<ALKP> subALKP = new HashSet<>();
Above both are pointng same ALKP and its Id then how can you get one and many object in same enity?
I have a REST API that will receive some customer data on the following format:
{
"customer_Id": 50,
"name": "name",
"company_name": "company_name",
"email": "email#provider.com",
"business_phone": "(00) 1111-2222",
"mobile_phone": "(00) 1111-2222",
"document": "123456789",
"state_registration_number": "ISENTO",
"state_registration_type": "NO_CONTRIBUTOR",
"city_registration_number": "ISENTO",
"classification": "AUTO",
"address": {
"street": "STREET NAME XXX",
"number": "NUMBER XX",
"complement": "COMPLEMENT",
"zip_code": "ZIP_CODE",
"neighborhood": "NEIGHBORHOOD",
"city": "CITY",
"state": "STATE"
}
}
I'd like to save this data on two tables: One table should contains the "main" customer data, and the other one should contais the customer's "address" data.
So, I defined the Customer entity as below:
#Data
#Entity(name = "X_CUSTOMERS")
public class Customer {
#Id
private int customer_Id;
#NotNull
private String name;
private String company_name;
private String email;
private String business_phone;
private String mobile_phone;
#NotNull
private String document;
private String state_registration_number;
private String state_registration_type;
private String city_registration_number;
#NotNull
private String classification;
#OneToOne(cascade = CascadeType.ALL)
private Address address;
}
And the Address entity as
#Data
#Entity(name = "X_ADDRESS")
public class Address {
#NotNull
private String street;
private String number;
private String complement;
private String zip_code;
private String neighborhood;
private String city;
private String state;
}
But, I couldn't realize how to create a relationship between them. Should I create a customer_id attribute on the Address entity? Should I define some additional Tags on Customer's address attribute? Note that I don't have a customer on the JSON data that is posted by the REST Client and, if a Customer is Update ou Deleted, the Address data should be Updated / Deleted also.
Sorry if this is a such trivial question. I'm learning the basics of JPA/Hibernate these days and your answer will guides me to the right direction to avoid things such 'reinventing the wheel'.
Thanks a lot!
If we consider Address to be a Value Object rather than entity then it can be mapped as below. In your case, it probably is correct to model it as a VO: if you were building a database of addresses then it could be considered an entity. See further here:
Value vs Entity objects (Domain Driven Design)
We can then make the address class an #Embeddable rather than an entity: it will not then have any identity of its own. To have the customer and address details stored in separate tables we can also use JPAs #SecondaryTable funtionality:
https://docs.oracle.com/javaee/7/api/javax/persistence/SecondaryTable.html
We have then the model classes as below. With these mappings your JSON updates will work as expected.
Customer:
#Data
#Table(name = "customers")
#SecondaryTable(name = "customer_addresses", pkJoinColumns={
#PrimaryKeyJoinColumn(name="customer_id",
referencedColumnName="customer_id")})
public class Customer {
protected static final String ADDRESS_TABLE_NAME = "customer_addresses";
// other fields
#Embedded
private Address address;
}
Address:
#Data
#Embeddable
public class Address {
#NotNull
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String street;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String number;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String complement;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String zip_code;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String neighborhood;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String city;
#Column(table = Customer.ADDRESS_TABLE_NAME)
private String state;
}
This is how i do it :
#OneToOne (fetch=FetchType.EAGER, cascade = CascadeType.ALL, optional = false)
#NotNull(message = "L'addresse du domicile est requise!", groups = Seventh.class)
#Getter
#Setter
private Address homeAddress;
No need for any inverse mapping and this lets me save a customer and his address in one fell swoop!
You need an ID for your address entity as well, something like :
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
#Getter
#Setter
private Long id;
I have the following classes: DepartmentMember and Account, mapped by a OneToOne relationship.
This is the DepartmentMember class:
#Entity(name="departmentmember")
#Table(name="departmentmember")
#Embeddable
public class DepartmentMember {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name="name", nullable=false)
private String nume;
#Column(name="lastName", nullable=false)
private String prenume;
#OneToOne(mappedBy="departmentMember",cascade=CascadeType.ALL,fetch=FetchType.LAZY, optional=false)
#JsonIgnore
private Account account;
public DepartmentMember() {}
public DepartmentMember(String nume, String prenume, String cNP, String email) {
super();
this.nume = nume;
this.prenume = prenume;
}
//getters and setters
}
And this is the Account class :
#Entity(name="users")
#Table(name="users")
public class Account {
#Id
private int id;
#Column(name="username", unique=true, nullable=false)
private String username;
#Column(name="password", nullable = false)
private String password;
#Column(name="authorities", nullable=false)
private String authorities;
#OneToOne(fetch=FetchType.EAGER)
#MapsId
#Embedded
private DepartmentMember departmentMember;
public Account() {}
public Account(String username, String password, String authorities) {
super();
this.username = username;
this.password = password;
this.authorities = authorities;
}
//getters and setters
}
I have defined an interface AccountRepository which extends the CrudRepository interface provided by Spring JPA.
What I want to do is define a query, which takes as a parameter a DepartmentMember id and retrieves the associated account for that member. Now this is how an Account object looks like:
{
"username": "Maria_Popescu",
"password": "4ec38c6e-2463-4562-99ba-9f6c2b4528c4",
"authorities": "ROLE_USER",
"departamentMember": {
"id": 2,
"nume": "Popescu",
"prenume": "Maria",
}
I tried using the findOne(int id) method, but it didn't work, so which is the correct approach to solve this?
Edit:
In the AccountRepository I have defined the following method :
Account findByDepartmentMemberId(int id) and I still get a not found error.
There was actually another problem in my controller. I managed to get it working by adding
Account findByDepartmentMemberId(#Param("id")int id);
in the AccountRepository
I have a JPA entity Person and an entity Team. Both are joined by an entity PersonToTeam. This joining entity holds a many-to-one relation to Person and one to Team. It has a multi-column key consisting of the ids of the Person and the Team, which is represented by an #EmbeddedId. To convert the embedded id back and forth to the request id I have a converter. All this follows the suggestion on Spring Data REST #Idclass not recognized
The code looks like this:
#Entity
public class PersonToTeam {
#EmbeddedId
#Getter
#Setter
private PersonToTeamId id = new PersonToTeamId();
#ManyToOne
#Getter
#Setter
#JoinColumn(name = "person_id", insertable=false, updatable=false)
private Person person;
#ManyToOne
#Getter
#Setter
#JoinColumn(name = "team_id", insertable=false, updatable=false)
private Team team;
#Getter
#Setter
#Enumerated(EnumType.STRING)
private RoleInTeam role;
public enum RoleInTeam {
ADMIN, MEMBER
}
}
#EqualsAndHashCode
#Embeddable
public class PersonToTeamId implements Serializable {
private static final long serialVersionUID = -8450195271351341722L;
#Getter
#Setter
#Column(name = "person_id")
private String personId;
#Getter
#Setter
#Column(name = "team_id")
private String teamId;
}
#Component
public class PersonToTeamIdConverter implements BackendIdConverter {
#Override
public boolean supports(Class<?> delimiter) {
return delimiter.equals(PersonToTeam.class);
}
#Override
public Serializable fromRequestId(String id, Class<?> entityType) {
if (id != null) {
PersonToTeamId ptid = new PersonToTeamId();
String[] idParts = id.split("-");
ptid.setPersonId(idParts[0]);
ptid.setTeamId(idParts[1]);
return ptid;
}
return BackendIdConverter.DefaultIdConverter.INSTANCE.fromRequestId(id, entityType);
}
#Override
public String toRequestId(Serializable id, Class<?> entityType) {
if (id instanceof PersonToTeamId) {
PersonToTeamId ptid = (PersonToTeamId) id;
return String.format("%s-%s", ptid.getPersonId(), ptid.getTeamId());
}
return BackendIdConverter.DefaultIdConverter.INSTANCE.toRequestId(id, entityType);
}
}
The problem with this converter is, that the fromRequestId method gets a null as id parameter, when a post request tries to create a new personToTeam association. But there is no other information about the payload of the post. So how should an id with foreign keys to the person and the team be created then? And as a more general question: What is the right approach for dealing many-to-many associations in spring data rest?
After running into the same issue I found a solution. Your code should be fine, except I return new PersonToTeamId() instead of the DefaultIdConverter if id is null in fromRequestId().
Assuming you are using JSON in your post request you have to wrap personId and teamId in an id object:
{
"id": {
"personId": "foo",
"teamId": "bar"
},
...
}
And in cases where a part of the #EmbeddedId is not a simple data type but a foreign key:
{
"id": {
"stringId": "foo",
"foreignKeyId": "http://localhost:8080/path/to/other/resource/1"
},
...
}