I have a following error:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`spindledb`.`section`, CONSTRAINT `FK_ftoru9cp83n512p9is8x3vo53` FOREIGN KEY (`scenario_id`) REFERENCES `scenario` (`scenario_id`))
Here are my classes:
Scenario:
#Entity
#Table(name = "scenario")
public class Scenario {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "scenario_id")
private int id;
#Column(name = "title", nullable = false)
private String title;
#NotNull
#DateTimeFormat(pattern = "dd/MM/yyyy")
#Column(name = "creation_date", nullable = false)
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
private LocalDate creationDate;
#ManyToOne
#LazyCollection(LazyCollectionOption.FALSE)
#JoinColumn(name = "id", nullable = false)
private User user;
#OneToMany(mappedBy = "scenario", orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
private Set<Plot> plotList = new HashSet<Plot>();
#OneToMany(mappedBy = "scenario", orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
private Set<Character> characterList = new HashSet<Character>();
#OneToMany(mappedBy = "scenario", cascade=CascadeType.ALL, orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
#OrderBy("sequence ASC")
private Set<Section> sectionList = new HashSet<Section>();
Section:
#Entity
#Table(name = "section")
public class Section {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "section_id")
private int id;
#Size(min = 4, max = 50)
#NotNull
#Column(name = "name")
private String name;
#NotNull
#Column(name = "type")
private String type = SectionType.TEXT.getSectionType();
#Column(name = "visibility")
private boolean visibility;
#NotNull
#Column(name = "sequence")
private int sequence;
#ManyToOne (cascade=CascadeType.ALL)
#LazyCollection(LazyCollectionOption.FALSE)
#JoinColumn(name = "scenario_id", nullable = false)
private Scenario scenario;
Controller:
#RequestMapping(value = { "/delete-{id}-scenario" }, method = RequestMethod.GET)
public String deleteScenario(#PathVariable int id) {
scenarioService.deleteScenarioById(id);
return "redirect:/home";
}
Scenario service:
#Service("scenarioService")
#Transactional
public class ScenarioServiceImpl implements ScenarioService {
#Autowired
private ScenarioDao dao;
#Override
public Scenario findById(int id) {
return dao.findById(id);
}
#Override
public void saveScenario(Scenario scenario) {
dao.saveScenario(scenario);
}
public void updateScenario(Scenario scenario) {
Scenario entity = dao.findById(scenario.getId());
if(entity!=null){
entity.setTitle(scenario.getTitle());
entity.setCreationDate(scenario.getCreationDate());
}
}
#Override
public void deleteScenarioById(int id) {
dao.deleteScenarioById(id);
}
Dao
#Repository("scenarioDao")
public class ScenarioDaoImpl extends AbstractDao<Integer, Scenario> implements ScenarioDao {
#Override
public Scenario findById(int id) {
return getByKey(id);
}
#Override
public void saveScenario(Scenario scenario) {
persist(scenario);
}
#Override
public void deleteScenarioById(int id) {
Query query = getSession().createSQLQuery("delete from scenario where id = :id");
query.setString("id", ""+id);
query.executeUpdate();
}
I understand that the problem is that there may be a Section that can not exist without scenario. Right now however section table in database is empty and I still can't remove Scenario. Thanks for advice
Deleting an entity via Query would bypass any Cascade settings you put via annotation.
I would suggest find the entity first by id, then delete the entity object:
Object scenario = session.load(Scenario.class, id);
if (scenario != null) {
session.delete(scenario);
}
use cascade=CascadeType.ALL with all #ManyToOne relations in class Scenario because if you are going to delete any Scenario from database it must not be referenced any where in data base.
the other way to delete is.
Serializable id = new Long(1); //your id
Object persistentInstance = session.load(Scenario.class, id);
if (persistentInstance != null) {
session.delete(persistentInstance);
}
Related
Im learning, and so far i created many to many bidirectional database - user can create many groups and group can have many users - and i cannot find a way for my GroupsController Post mapping to work, from my understanding, it requires to get firstly Users id, in order to set the right relationship in Join table for Group, because the relationship should be set only when user create/join group, not when user create sign up procedure. Postman throws 500 and intelliJ:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "java.lang.Long.longValue()" because the return value of "com.ilze.highlight.entity.Groups.getId()" is null] with root cause
java.lang.NullPointerException: Cannot invoke "java.lang.Long.longValue()" because the return value of "com.ilze.highlight.entity.Groups.getId()" is null
I use lombok - #Data, #Getter, therefore getId() should be available for use from Group class. My GroupsController with POST mapping when user decides to create a new group:
#RestController
#RequestMapping("api/groups") // pre-path
public class GroupsController{
#Autowired
private GroupsService groupsService;
#Autowired
private UserService userService;
#Autowired
private final GroupsRepository groupsRepository;
#Autowired
private UserRepository userRepository;
public GroupsController(GroupsRepository groupsRepository) {
this.groupsRepository = groupsRepository;
}
#GetMapping("/all-groups")
public List<Groups> getGroups(){
return (List<Groups>) groupsRepository.findAll();
}
#PostMapping("/user/{usersId}/create-group")
public ResponseEntity<Groups> createGroup(#PathVariable(value = "usersId") Long usersId, #RequestBody Groups groupRequest){
Groups group = userRepository.findById(usersId).map(users -> {
long groupsId = groupRequest.getId();
// add and create new group
users.addGroup(groupRequest);
return groupsRepository.save(groupRequest);
}).orElseThrow(() -> new ResourceNotFoundException("Not found user with id = " + usersId));
return new ResponseEntity<>(group, HttpStatus.CREATED);
}
}
Group database class:
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Table(name = "group_collection")
public class Groups {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name ="group_name", unique = true, nullable = false, length = 20)
private String groupName;
#Column(name = "size", nullable = false)
private int size;
#Column(name = "strict", nullable = false)
private boolean strict;
#Column(name = "open", nullable = false)
private boolean open;
#Column(name ="description", length = 300)
private String description;
#Column(name = "create_time", nullable = false)
private LocalDateTime createTime;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.DETACH,
CascadeType.REFRESH
},
mappedBy = "groups")
#JsonIgnore
private Set<User> users = new HashSet<>();
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
And Users class for database:
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username", unique = true, nullable = false, length = 100)
private String username;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "email", nullable = false)
private String email;
#Column(name = "create_time", nullable = false)
private LocalDateTime createTime;
#Enumerated(EnumType.STRING)
#Column(name = "role", nullable = false)
private Role role;
#Transient
private String accessToken;
#Transient
private String refreshToken;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.DETACH,
CascadeType.REFRESH
})
#JoinTable(name = "groups_x_user",
joinColumns = { #JoinColumn(name = "users_id") },
inverseJoinColumns = {#JoinColumn(name = "groups_id")})
private Set<Groups> groups = new HashSet<>();
public void addGroup(Groups group) {
this.groups.add(group);
group.getUsers().add(this);
}
public void removeGroup(long id){
Groups group = this.groups.stream().filter(g ->
g.getId() == id).findFirst().orElse(null);
if(group != null){
this.groups.remove(group);
group.getUsers().remove(this);
}
}
For reference my GroupsService implementation:
#Service
public class GroupsServiceImpl implements GroupsService{
private final GroupsRepository groupsRepository;
public GroupsServiceImpl(GroupsRepository groupsRepository) {
this.groupsRepository = groupsRepository;
}
#Override
public Groups saveGroup(Groups group) {
group.setCreateTime(LocalDateTime.now());
return groupsRepository.save(group);
}
#Override
public Optional<Groups> findByGroupName(String groupName) {
return groupsRepository.findByGroupName(groupName);
}
}
You need to persist the object from request. And since you have Many-2-Many relation, you can insert related object from both sides. In your case: just add existing user to the newly created group
The method will look something like that:
#PostMapping("/user/{usersId}/groups")
public ResponseEntity<Groups> createGroup(#PathVariable(value = "usersId") Long usersId, #RequestBody Groups groupRequest) {
Groups createdGroup = userRepository.findById(usersId)
.map(user -> {
groupRequest.setId(null); // ID for new entry will be generated by entity framework, prevent override from outside
groupRequest.getUsers().add(user); // add relation
return groupsRepository.save(groupRequest);
}).orElseThrow(() -> new ResourceNotFoundException("Not found user with id = " + usersId));
return new ResponseEntity<>(createdGroup, HttpStatus.CREATED);
}
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 something similar to this:
#Entity
#Table(name = "claim", schema = "test")
public class Claim implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idClaim", unique = true, nullable = false)
private Integer idClaim;
#OneToOne(mappedBy = "claim", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JsonManagedReference
private ClaimReturnInfo claimReturnInfo;
#Column(name = "notes")
private String notes;
// Getters and setters
}
#Entity
#Table(name = "claim_returninfo", schema = "test")
public class ClaimReturnInfo implements Serializable {
#Id
#Column(name = "Claim_idClaim")
private Integer id;
#MapsId("Claim_idClaim")
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Claim_idClaim")
#JsonBackReference
private Claim claim;
#Column(name = "description")
private String description;
// Getters and setters
}
ClaimReturnInfo Id is not autogenerated because we want to propagate the Id from its parent (Claim). We are not able to do this automatically and we are getting this error: ids for this class must be manually assigned before calling save() when 'cascade' is executed in ClaimReturnInfo .
Is it possible to map Claim Id into ClaimReturnInfo Id or should we do this manually?
Even if we set this ID manually on claimReturnInfo and we can perform updates, we still get this error when trying to create a new Claim:
// POST -> claimRepository.save() -> Error
{
"notes": "Some test notes on a new claim",
"claimReturnInfo": {
"description": "Test description for a new claimReturnInfo"
}
}
In the ServiceImplemetation:
#Override
#Transactional
public Claim save(Claim claim) throws Exception {
if(null != claim.getClaimReturnInfo()) {
claim.getClaimReturnInfo().setId(claim.getIdClaim());
}
Claim claimSaved = claimRepository.save(claim);
return claimSaved;
}
I have tried using the following mappings and from your comments it was apparent that Json object is populated correctly.
I have noticed that the annotation #MapsId is the culprit.If you check the documentation of #MapsId annotation it says
Blockquote
The name of the attribute within the composite key
* to which the relationship attribute corresponds. If not
* supplied, the relationship maps the entity's primary
* key
Blockquote
If you change #MapsId("Claim_idClaim") to #MapsId it will start persisting your entities.
import javax.persistence.*;
#Entity
#Table(name = "CLAIM")
public class Claim {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idClaim", unique = true, nullable = false)
private Long idClaim;
#Column(name = "notes")
private String notes;
#OneToOne(mappedBy = "claim", cascade = CascadeType.ALL, optional = false)
private ClaimReturnInfo claimReturnInfo;
public Long getIdClaim() {
return idClaim;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public ClaimReturnInfo getClaimReturnInfo() {
return claimReturnInfo;
}
public void setClaimReturnInfo(ClaimReturnInfo claimReturnInfo) {
if (claimReturnInfo == null) {
if (this.claimReturnInfo != null) {
this.claimReturnInfo.setClaim(null);
}
} else {
claimReturnInfo.setClaim(this);
}
this.claimReturnInfo = claimReturnInfo;
}
}
package com.hiber.hiberelations;
import javax.persistence.*;
#Entity
#Table(name = "CLAIM_RETURN_INFO")
public class ClaimReturnInfo {
#Id
#Column(name = "Claim_idClaim")
private Long childId;
#Column(name = "DESCRIPTION")
private String description;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Claim_idClaim")
private Claim claim;
public Long getChildId() {
return childId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Claim getClaim() {
return this.claim;
}
public void setClaim(Claim claim) {
this.claim = claim;
}
}
When I'm trying to save an U object I got next exception:
org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [com.roc.domain.A.user]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.roc.domain.A.user]
I have two tables:
1. user that columns are id(auto incr, primary), name.
2. contact that columns are id, user_id(that is foreign key -> user.id) and address.
#Entity
#Table(name = "a")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name="address")
private String address;
#OneToOne
#MapsId
private U user;
public A() {
}
// getters and setters
}
#Entity
#Table(name = "u")
public class U {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name="username")
private String userName;
#JoinColumn(name = "user_id", referencedColumnName = "id")
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private A a;
public U(){};
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Test
public void simpleCrudTest() {
U user = new U("name", new A("address"));
uRepository.save(user);
}
}
You have set the cascade correctly however because the relationship is bi-directional you need to set both sides in the in-memory model.
#Test
public void simpleCrudTest() {
U user = new U("name", new A("address"));
//will work when this is added
a.setUser(user);
uRepository.save(user);
}
Otherwise, as the error states, A has a null reference for user on save.
Edit: To save using a single repository save call.
#Entity
#Table(name = "a")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "address")
private String address;
#OneToOne
#MapsId
private U user;
public A() {
}
}
#Entity
#Table(name = "u")
public class U {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#JoinColumn(name = "user_id", referencedColumnName = "id")
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private A a;
public U() {
};
// method to manage the bidirectional association
public U addToA(A a) {
this.a.add(a);
a.setUser(this);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Test
public void simpleCrudTest() {
U user = new U();
user.addToA(new A("address"));
user.setUserName("username");
uRepository.save(user);
}
}
Also, you refer to this link.
inserting values into multiple tables using hibernate
You have to save A first, Then set saved A to U and save U.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Autowired
private ARepository aRepository;
#Test
#Trascational
public void simpleCrudTest() {
A a = new A();
a.setAddress("address");
a = aRepository.save(a);
U user = new U("name", a);
uRepository.save(user);
}
}
I write my first java application to read rss stream and use spring, spring-data, hibernate.
My models.
RssFeed:
#Entity(name = "RssFeed")
#Table(name = "FEED")
#JsonIgnoreProperties({"rssChannel"})
public class RssFeed {
#Id
#GeneratedValue
#Column
private Integer id;
#Column(unique = true)
#Index(name = "title_index")
private String title;
#Column
#URL
private String link;
#Column
private String description;
#Column
private String content;
#Column
#Temporal(TemporalType.TIMESTAMP)
private Date pubDate;
#Column
#Temporal(TemporalType.TIMESTAMP)
private Date updateDate;
#ManyToOne
#JoinColumn(name = "channelId")
private RssChannel rssChannel;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "feed_category",
joinColumns = {#JoinColumn(name = "feed_id", nullable = false, updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "category_id", nullable = false, updatable = false)})
private Set<RssCategory> rssCategories = new LinkedHashSet<RssCategory>();
}
RssChannel:
#Entity(name = "RssChannel")
#Table(name = "Channel",
uniqueConstraints = #UniqueConstraint(columnNames = {"link"}))
#JsonIgnoreProperties({"feeds"})
public class RssChannel implements Serializable{
#Id
#GeneratedValue
#Column
private Integer id;
#Column
private String title;
#Column(unique = true)
#org.hibernate.validator.constraints.URL
private String link;
#Column
#org.hibernate.validator.constraints.URL
private String image;
#Column
private String description;
#OneToMany(mappedBy = "rssChannel", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RssFeed> feeds = new LinkedList<RssFeed>();
}
And RssCategory:
#Entity(name = "RssCategory")
#Table(name = "CATEGORY")
#JsonIgnoreProperties({"rssFeeds"})
public class RssCategory {
#Id
#GeneratedValue
#Column
private Integer id;
#Column(unique = true)
private String title;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "rssCategories")
public Set<RssFeed> rssFeeds = new LinkedHashSet<RssFeed>();
}
I use CrudRepository for manipulation with data. When save RssFeed without many to many it`s ok:
RssChannel channel = rssChannelService.get(url.toString());
rssFeed.setRssChannel(channel);
rssFeedService.save(rssFeed);
But when i add RssCategory:
rssCategory rssCategory = rssCategoryService.findOrCreate("test");
rssFeed.getRssCategories().add(rssCategory);
rssFeedService.save(rssFeed);
get exception: rg.hibernate.PersistentObjectException: detached entity passed to persist: RssCategory.
My RssFeedServiceImpl:
#Service
public class RssFeedServiceImpl implements RssFeedService {
#Autowired
private RssChannelDAO rssChannelDAO;
#Autowired
private RssFeedDAO rssFeedDAO;
#Override
public Page<RssFeed> findAll(Pageable pageable) {
return rssFeedDAO.findAll(pageable);
}
#Override
public Page<RssFeed> findAll(int rssChannelId, Pageable pageable) {
RssChannel rssChannel = rssChannelDAO.findOne(rssChannelId);
return rssFeedDAO.findByRssChannel(rssChannel, pageable);
}
#Override
public RssFeed get(String title) {
return rssFeedDAO.findByTitle(title);
}
#Override
public RssFeed save(RssFeed rssFeed) {
return rssFeedDAO.save(rssFeed);
}
}
And RssCategoryServiceImpl:
#Service
public class RssCategoryServiceImpl implements RssCategoryService {
#Autowired
RssCategoryDAO rssCategoryDAO;
#Override
public RssCategory findOrCreate(String title) {
RssCategory category = rssCategoryDAO.findByTitle(title);
if (category == null) {
category = new RssCategory();
category.setTitle(title);
category = rssCategoryDAO.save(category);
}
return category;
}
}
How save many to many?
You probably need to save your RssCategory first, in order to have an ID to store in feed_category table. This last save will be automatically made when you make the assignment:
rssFeed.getRssCategories().add(rssCategory);
but first you need to do:
rssFeedService.save(rssCategory);
Probably you'll need to put this operations within a transaction.