I have two classes: MenuItem.java and Tag.java. I am using the Hibernate implementation of JPA, and using PagingAndSortingRepository. After creating a bunch of dummy MenuItems and Tags, these are displayed in my local database and can be accessed with either: localhost:8080/api/menuItems or localhost:8080/api/tags. Additionally, as described by my annotations in the files below, there exists a ManyToMany relationship between these two objects, and a Menu_Items_Tags table with the appropriate entries is also created in my database without any issue.
The problem I run into is that every time I attempt to access a given menu item's list of tags via: localhost:8080/api/menuItems/1/tags for example, I receive a 500 error and additionally have a repeating error code like this:
: HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext#726b0462<rs=HikariProxyResultSet#1877641821 wrapping Result set representing update count of 6>
Is there something I am doing wrong in either trying to request the Tags of a MenuItem or am I incorrectly setting up my #ManyToMany relationship?
MenuItem.java:
#Data
#Entity
public class MenuItem implements Serializable {
private static final long serialVersionUID = 1132661429342356177L;
public MenuItem() {
}
public MenuItem(String name, Double price, Integer inventory, String description, Set<Tag> tags) {
this.name = name;
this.price = price;
this.inventory = inventory;
this.description = description;
this.tags = tags;
}
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(name = "name", nullable = false, unique = true)
private String name;
#Column(name = "price", nullable = false)
private Double price;
#Column(name = "inventory", nullable = false)
private Integer inventory;
#Column(name = "description", nullable = false)
private String description;
#OneToMany(mappedBy = "menuItem")
private Set<Order> orders;
#ManyToMany
#JoinTable(
name="menu_item_tags",
joinColumns = #JoinColumn(name = "menu_item_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "tag_id", referencedColumnName = "id"))
#JsonManagedReference
private Set<Tag> tags;
}
Tag.java:
#Data
#Entity
public class Tag implements Serializable {
private static final long serialVersionUID = 1132661429342356176L;
public Tag() {
}
public Tag(String name) {
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name", unique = true)
private String name;
// #JsonIgnore
#ManyToMany(mappedBy = "tags")
#JsonBackReference
private Set<MenuItem> menuItems;
}
Related
I have a table with n x n relationship. That means i have a code that is repeated with only a "brand" column being different between them. The code is not the ID.
I need to have one distinct class that contains all repeatable values and with a list that has all the unique brand column values.I cannot show most of the code samples because of my company security policies. But the entity below is a mock of what i'm seeking.
`
#Entity
#Table(name = "Table")
public class Teste {
#Column(name = "CODE", insertable = false, updatable = false)
private String code;
#Column(name = "NAME")
private String name;
#Column(name = "BRAND")
private String brand;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "BRAND", joinColumns = { #JoinColumn(name = "CODE"), #JoinColumn(name = "BRAND")})
private Set<Model> brands = new HashSet<>();
#Column(name = "UPDATE_DATE")
private Date update_date;
#Column(name = "EDITOR")
private String editor;
#Id
#Column(name = "ID")
private int id;
}
`
Problem
I am trying to store an object in my Postgres database. This consists of the Order.class, (List) OrderDevice.class, and a Department.class.
The important thing is that the OrderDevices are always stored new in the DB, but a Department may already exist.
When I try to save the object to my database using save I get the following error message: (shown below)
I get the error message "detached entity passed to persist: com.niclas.model.OrderDevice" if the department does not exist yet, if the department exists the error message looks like this: "detached entity passed to persist: com.niclas.model.Department".
Solution attempts
This solution cannot be used because I do not use bidirectional mapping.
(I don't want to use a bidirectional mapping because I want to access the departments without an order.)
I also tried to change the Cascade types to MERGE like in this solution
I also tried using #Transactional on the method
I also tried to save the children in the database first and then the parent like this:
departmentRepository.save(order.getDepartment()); orderDeviceRepository.saveAll(order.getDevices()); orderRepository.save(order);
I hope I have described my good enough and I am happy about suggestions for solutions
Error.log
The log can be viewed here. (The formatting did not work here)
Order.class
#Entity
#Table(name = "orders")
public class Order extends AuditModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "order_id")
private String orderId;
#Column(name = "department_id")
private long departmentId;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "department", referencedColumnName = "id")
private Department department;
#JsonProperty("deviceList")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", referencedColumnName = "order_id")
private List<OrderDevice> devices;
#JsonProperty("forename")
#Column(name = "sender_forename")
private String senderForename;
#JsonProperty("surname")
#Column(name = "sender_surname")
private String senderSurname;
#Column(name = "notes", columnDefinition = "TEXT")
private String notes;
#Column(name = "month")
private int month;
#Column(name = "year")
private int year;
public Order() {
}
... Getter/Setters
}
OrderDevice.class
#Entity
#Table(name = "order_devices")
public class OrderDevice extends AuditModel{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column( name = "order_id", insertable = false, updatable = false )
private String orderId;
#Column(name = "device_id")
private long deviceId;
#Column(name = "device_name")
private String deviceName;
#Column(name = "priceName")
private String priceName;
#Column(name = "price")
private double price;
#Column(name = "count")
private int count;
public OrderDevice() {
}
... Getters/Setters
}
Department.class
#Entity
#Table(name = "departments")
public class Department {
//TODO add Form Validation
//TODO better Naming for From Attributes on Frontend and Backend
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "department_name")
private String department;
#Column(name = "contact_person_forename")
private String forename;
#Column(name = "contact_person_surname")
private String surname;
#Column(name = "contact_person_mail")
private String mail;
#Column(name = "street")
private String street;
#Column(name = "house_number")
private String houseNumber;
#Column(name = "location")
private String location;
#Column(name = "postal_code")
private int postalCode;
#Column(name = "country")
private String country;
#Column(name = "auto_send_invoice")
private boolean autoSend;
#Column(name = "registered")
private boolean registered;
public Department() {
}
... Getter/Setters
}
OrderController.class
#Slf4j
#RestController
public class OrderController {
private final DepartmentRepository departmentRepository;
private final OrderRepository orderRepository;
private final OrderDeviceRepository orderDeviceRepository;
public OrderController(OrderRepository orderRepository, DepartmentRepository departmentRepository,
OrderDeviceRepository orderDeviceRepository) {
this.orderRepository = orderRepository;
this.departmentRepository = departmentRepository;
this.orderDeviceRepository = orderDeviceRepository;
}
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody Order order) throws JsonProcessingException {
order.setOrderId(order.generateOrderId());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
order.getDevices().forEach(orderDevice -> {
orderDevice.setOrderId(order.getOrderId());
});
//departmentRepository.save(order.getDepartment());
//orderDeviceRepository.saveAll(order.getDevices());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
Update
If the objects are created in this way, no error will occur and the order will be successfully saved in the database.
However, I don't understand why it works this way and not via ObjectMapper. Does anyone know why?
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody JsonNode jsonNode) throws JsonProcessingException {
Order order = new Order();
JsonNode departmentJson = jsonNode.get("department");
Department department;
if ( departmentJson.get("id").canConvertToInt() ) {
department = departmentRepository.findDepartmentById(departmentJson.get("id").asInt());
} else {
department = new Department();
department.setDepartment(departmentJson.get("department").asText());
department.setForename(departmentJson.get("forename").asText());
department.setSurname(departmentJson.get("surname").asText());
department.setMail(departmentJson.get("mail").asText());
department.setStreet(departmentJson.get("street").asText());
department.setHouseNumber(departmentJson.get("houseNumber").asText());
department.setLocation(departmentJson.get("location").asText());
department.setPostalCode(departmentJson.get("postalCode").asInt());
department.setCountry(departmentJson.get("country").asText());
department.setAutoSend(departmentJson.get("autoSend").asBoolean());
department.setRegistered(departmentJson.get("registered").asBoolean());
}
order.setDepartment(department);
order.setOrderId(order.generateOrderId());
order.setDepartmentId(department.getId());
List<OrderDevice> orderDevices = new ArrayList<>();
JsonNode devices = jsonNode.get("deviceList");
for (JsonNode node : devices) {
//TODO replace this mess with objectMapper
if (node.has("count") && node.get("count").asInt() != 0){
OrderDevice device = new OrderDevice();
device.setOrderId(order.getOrderId());
device.setDeviceId(node.get("id").asLong());
device.setDeviceName(node.get("deviceName").asText());
device.setPriceName(node.get("priceName").asText());
device.setPrice(node.get("price").asDouble());
device.setCount(node.get("count").asInt());
orderDevices.add(device);
}
}
order.setDevices(orderDevices);
order.setSenderForename(jsonNode.get("forename").asText());
order.setSenderSurname(jsonNode.get("surname").asText());
order.setNotes(jsonNode.get("notes").asText());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
You can try to use instead of orderRepository.save(order) use orderRespostiory.saveOrUpdate(order).
I have 4 models:
Blueprint, that describes a single type of Item:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="name")
private String name;
#ElementCollection
#CollectionTable(name = "BLUEPRINT_TAG", joinColumns = #JoinColumn(name = "blueprint"))
private List<String> tags;
#OneToMany(mappedBy = "blueprint", cascade = CascadeType.ALL, orphanRemoval = true)
private List<BlueprintProperty> properties;
#OneToMany(mappedBy = "blueprint", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonIgnore
private List<Item> items;
#Column(name = "width")
private Float width;
#Column(name = "height")
private Float height;
#Column(name = "imagePath")
private String imagePath;
#Column(name = "zPosition")
private long zPosition;
BlueprintProperty, that describes what information can be entered for each type of Item.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "blueprint")
#JsonIgnore
private Blueprint blueprint;
#Column(name = "name")
private String name;
#Enumerated(EnumType.STRING)
private PropertyType type;
#Column(name = "data")
#Convert(converter = JpaJsonConverter.class)
private ObjectNode data;
#OneToMany(mappedBy = "property", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonIgnore
private List<ItemProperty> itemProperties;
Then there's Item:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "parent")
//#Nullable
private Item parent;
#Column(name = "pos_x")
private Float posX;
#Column(name = "pos_y")
private Float posY;
#Column(name = "transformation")
private String transformation;
#ManyToOne
#JoinColumn(name = "blueprint")
private Blueprint blueprint;
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
#JsonIgnore
private List<Item> children;
#OneToMany(mappedBy = "item", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ItemProperty> properties;
And finally, ItemProperty, that holds foreign keys referencing Item the specific property belongs to, and BlueprintProperty it's describing:
#Id
#ManyToOne
#JoinColumn(name = "item")
#JsonIgnore
private Item item;
#Id
#ManyToOne
#JoinColumn(name = "property")
#JsonIgnore
private BlueprintProperty property;
#Column(name = "value")
private String value;
Now, I'm not exactly happy with this setup, but that's what I have to work with and unless completely necessary I shouldn't change anything. What I'm trying to do is create a Query that takes a number of Item properties and returns all Items which properties match the query.
Using Query by example sounded promising, but I found out that only SingularAttribute properties can currently be used for property matching.. So that killed that approach, and I don't think I can use native query, because a certain Item can have N properties, and you should be able to enter a query that searches by a single property, up to N properties.
Can someone suggest a way to perform the search I need and get a List of Items matching whose properties matched those entered as a result?
I have a rest api that accept POST to save Candidates.
This the Object coming from POST:
{
"name": "renato",
"email":"rena#gmail.com",
"github":"renato.github.com",
"languages":[{"name":"Java"}],
"frameworks":[{"name":"Spring"}],
"linkedin":"renato.linkedin.com",
"motto":"the best",
"password":"1234"
}
then I do a Call to persist:
#Transactional
#Override
public boolean addCandidate(Candidate candidate) {
if (candidateRepository.findCandidateByEmail(candidate.getEmail()) == null) {
candidateRepository.save(candidate);
return true;
} else {
return false;
}
}
When trying to save (automatically) the Languages and the Frameworks, I would like to check in database if there's already a entry, and if exists, update only
the correspondent relational table.
Here's the Pojos:
CANDIDATE:
#Entity
#Table(name = "candidates")
public class Candidate {
#Id
#Column(name = "candidate_id")
#GeneratedValue
private Long cadet_Id;
#Column (name = "email")
private String email;
#Column (name = "name")
private String name;
#Column(name = "password")
private String password;
#Column (name = "motto")
private String motto;
#Column (name = "github")
private String github;
#Column (name = "linkedin")
private String linkedin;
#ManyToMany (cascade = {CascadeType.ALL})
#JoinTable(name = "candidate_languages",
joinColumns = #JoinColumn(name = "language_id"),
inverseJoinColumns = #JoinColumn(name = "candidate_id"))
private Set<Languages> languages;
#ManyToMany (cascade = {CascadeType.ALL})
#JoinTable(name = "candidate_frameworks",
joinColumns = #JoinColumn(name = "framework_id"),
inverseJoinColumns = #JoinColumn(name = "candidate_id"))
private Set<Frameworks> frameworks;
LANGUAGES
#Entity
#Table(name = "languages")
public class Languages {
#Id
#Column(name = "language_id")
#GeneratedValue
private Long language_Id;
#Column (name = "name")
private String name;
FRAMEWORKS:
#Entity
#Table(name = "Frameworks")
public class Frameworks {
#Id
#Column(name = "framework_id")
#GeneratedValue
private Long framework_Id;
#Column (name = "name", unique = true)
private String name;
I'm Using spring data and the default Repositorys for each class:
public interface FrameworkRepository extends CrudRepository<Frameworks,Long{}
Thanks for the help.
I'm creating a MySQL database as followed :
database design
the Country and Province tables are pre-filled with data. I have the application running and can get stuff no problem, and also the join table person_has_address works when getting.
however, when I insert data using post I want to be able to set the ID of the province, and let spring data jpa just add that number to add_pro_id in the Address table. For example, when I post the following json:
{ "firstName":"bilbo", "lastName":"baggings", "address":{"street":"streetName", "streetNum":3, "zipcode":"1337GG", "city":"TheCity", "province":{"name":"aProvinceName"}} }
jpa should see that aProvinceName exists and grab that id and add that to add_pro_id.
Now it just insert aProvinceName as new value in province and add the new id to add_pro_id.
The person class:
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="per_id")
private int id;
#Column(name="per_name")
private String firstName;
#Column(name="per_surname")
private String lastName;
#Column(name="per_birth_date")
private String birthDate;
#Column(name="per_fax")
private String fax;
#Column(name="per_phone")
private String phone;
#Column(name="per_email")
private String email;
#OneToOne(optional = false, cascade = CascadeType.ALL)
#JoinTable(name="person_has_address", joinColumns = {#JoinColumn(name="pha_per_id", referencedColumnName = "per_id")}, inverseJoinColumns = {#JoinColumn(name="pha_add_id", referencedColumnName = "add_id")})
private Address address;
// getters and setters
This is the person repository:
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findByLastName(#Param("name") String name);
}
This is the address class:
#Entity
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="add_id")
private int id;
#Column(name = "add_street")
private String street;
#Column(name="add_street_num")
private int streetNum;
#Column(name="add_zip")
private String zipcode;
#Column(name="add_city")
private String city;
#JoinColumn(name="add_pro_id", referencedColumnName = "pro_id")
#ManyToOne(optional=false, cascade = CascadeType.ALL)
private Province province;
// getters and setters
Province class:
#Entity
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="pro_id")
private int id;
#Column(name="pro_name")
private String name;
#ManyToOne
#JoinColumn(name="pro_cou_id")
private Country country;
// getters and setters
And lastly country class:
#Entity
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="cou_id", insertable = false, updatable = false)
private int id;
#Column(name="cou_name", insertable = false, updatable = false)
private String name;
// getters and setters
I've tried adding insertable = false and updatable = false, but the application then just inserts NULL values in my database. I've also tried working with #primarykeyjoins, but to no success.
if anyone knows how I should tackle this problem I would much appreciate it!
Thanks in advance.