JPA not clearing a Set iterator when saving in OneToMany relationship - java

I have a basic SpringBoot app. using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.
I have this domain class:
Entity
#DiscriminatorValue("sebloc")
public class SeblocDevice extends Device {
/**
*
*/
private static final long serialVersionUID = 1L;
public SeblocDevice() {
super();
}
public SeblocDevice(String deviceKey, String devicePAC) {
super(deviceKey, devicePAC);
}
#OneToMany(mappedBy = "device", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<DeviceDriver> driverDevices = new HashSet<>();
public Set<DeviceDriver> getDriverDevices() {
return driverDevices;
}
public void setDriverDevices(Set<DeviceDriver> driverDevices) {
this.driverDevices = driverDevices;
}
public void clearDriverDevices() {
for (DeviceDriver deviceDriver : deviceDrivers) {
deviceDriver.setDriver(null);
driverDevices.remove(deviceDriver);
}
public void removeDriverDevice(DeviceDriver deviceDriver) {
deviceDriver.setDriver(null);
driverDevices.remove(deviceDriver);
}
}
...
}
and this other domain object
#Entity
#Table(name = "t_device_driver")
public class DeviceDriver implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public DeviceDriver() {
}
public DeviceDriver (SeblocDevice device, Driver driver) {
this.device = device;
this.driver = driver;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "device_id")
private SeblocDevice device;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "driver_id")
private Driver driver;
public SeblocDevice getDevice() {
return device;
}
public void setDevice(SeblocDevice device) {
this.device = device;
}
public Driver getDriver() {
return driver;
}
public void setDriver(Driver driver) {
this.driver = driver;
}
}
and this JUnit test, where in the last test I was excepting 1 driver but I got 2 (clear all the drivers, and add 1)
#Test
public void testUpdateAuthorizedDriver() {
SeblocDevice seblocDevice = (SeblocDevice) deviceService.findById((long)1);
assertEquals (1,seblocDevice.getDriverDevices().size());
Driver authorizedDriver = (Driver) driverService.findById((long)2);
DeviceDriver dd = new DeviceDriver (seblocDevice, authorizedDriver);
DeviceDriver ddToRemove = seblocDevice.getDeviceDrivers().iterator().next();
seblocDevice.removeDriverDevice(ddToRemove);
seblocDevice.clearDriverDevices()
seblocDevice.getDriverDevices().clear();
seblocDevice.getDriverDevices().add(dd);
deviceService.save(seblocDevice);
assertEquals (1, seblocDevice.getDriverDevices().size());
assertEquals (1, Iterators.size(deviceService.findSeblocDeviceAll().iterator()));
SeblocDevice seblocDeviceRetrieved = deviceService.findSeblocDeviceAll().iterator().next();
assertEquals (1, seblocDeviceRetrieved.getDriverDevices().size());
}
I also tried to create a method in the service level
public interface DeviceDriverRepository extends CrudRepository<DeviceDriver, Long> {
}
#Transactional
public SeblocDevice cleanDrivers (SeblocDevice seblocDevice) {
deviceDriverRepository.delete(seblocDevice.getDeviceDrivers());
seblocDevice.getDeviceDrivers().clear();
seblocDevice.setDeviceDrivers(null);
return seblocDeviceRepository.save (seblocDevice);
}
and then deviceService.cleanDrivers(seblocDevice);
but the drivers appears again

crizzis is right you have to set device to null.
The best way to keep a bidirectional association consistent is to create convenience methods like:
public void addDriverDevice(DeviceDriver deviceDriver) {
deviceDriver.setDriver(deviceDriver);
driverDevices.add(deviceDriver);
}
public void removeDriverDevice(DeviceDriver deviceDriver) {
deviceDriver.setDriver(null);
driverDevices.remove(deviceDriver);
}
And if you want to clear all
public void clearDriverDevices() {
for (DeviceDriver deviceDriver : deviceDrivers) {
deviceDriver.setDriver(null);
driverDevices.remove(deviceDriver);
}
}

For your code to work as you expect, you need to add the orphanRemoval=true parameter in #OneToMany relationship in SeblocDevice.driverDevices attribute as shown below:
#OneToMany(mappedBy = "device", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<DeviceDriver> driverDevices = new HashSet<>();
To clearly understand the JPA mapping. You need to keep in mind that in a relationship, there is always the owner side.
For example, in an #OneToMany vs. #ManyToOne relationship, #ManyToOne is the owner because it has the reference to the other entity.
Basically the Entity Manager only cares for changes on the owner side, ie if you invoke DeviceDriver.setDevice(null), the removal will be performed. But the opposite
(SeblocDevice.getDriverDevices().clear()) is not true.
For this reason, there is the orphanRemoval parameter, which is self explanatory. When this parameter is assigned, the Entity Manager will now control the elements of the collection as an owner, and a SeblocDevice.getDriverDevices().clear() will remove the DeviceDrivers in database that are not in the SeblocDevice.getDriverDevices collection, even though DeviceDriver.device is not null.

Related

Java listen on entity's child collection change

I'm trying to detect if entity's relation is updated, but nothing that I've tried or found is working.
What I have for short is like:
#Entity
#EntityListeners(KidsListener.class)
#Table(name = "user")
public class User {
#Setter(AccessLevel.PRIVATE)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Access(AccessType.PROPERTY)
private Long id;
#Setter(AccessLevel.NONE)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Kid> kids = new HashSet<>(0);
#Version
#Column(name = "version")
private Long version;
}
I've tried both solutions - EntityListeners and implementing PostCollectionUpdateEventListener
public class KidsListener implements PostCollectionUpdateEventListener {
#PostPersist
#PostUpdate
private void afterAnyUpdate(User user) {
// do something
}
#Override
public void onPostUpdateCollection(PostCollectionUpdateEvent event) {
PersistentCollection collection = event.getCollection(); // do something
}
}
Nothing of those works, even version for parent User entity isn't changing.
The save part is just something like:
User user = userRepository.findById(id);
for(Kid kid : mappedFromDto.getKids()){
user.getKids().add(kid);
}
userRepository.save(user);
I can't listen on Kid change because I want to send out (in one request) a pack of all changes in User's kids.
Sorry, but I have to ask. Did you register the listener?
public class CustomIntegrator implements Integrator {
public void integrate(
Metadata metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
listenerRegistry.appendListeners(
EventType.POST_COLLECTION_UPDATE,
new KidsListener()
);
}
#Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
}
}
You also have to create a META-INF/services/org.hibernate.integrator.spi.Integrator file that contains the fully qualified name of the integrator class.
After that, you should receive all events.
Please note that there are other events as well, like POST_COLLECTION_REMOVE and POST_COLLECTION_RECREATE that you might have to listen for as well to capture inserts/deletes.

JpaRepository findOne(id) returns null

This is my first post here, I've been searching for a long time here but I didn't found a problem that seemed similar.
When I use JpaRepository function findOne(id) for one of my classes, it returns null. As if no row had been found for this id.
Of course the database row with this id exists.
Also my class mapping seems right.
I don't understand because I already used findOne() for other classes and I never had any problem.
Anyone can tell me what can be the source of this problem, please ? That would be nice !
This is my DAO :
#Transactional
public interface OrderDetailDAO extends JpaRepository<OrderDetail, Integer>
{
}
This is my Model :
#Entity
#Table(name = "order_detail", schema = "", catalog = AppConfig.databaseSchema)
public class OrderDetail implements Serializable {
private int idOrderDetail;
private Order order;
private Preorder preorder;
private UnitType unitType;
private Sale sale;
private DeliveryStatusType deliveryStatusType;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_Order_Detail")
public int getIdOrderDetail() {
return idOrderDetail;
}
public void setIdOrderDetail(int idOrderDetail) {
this.idOrderDetail = idOrderDetail;
}
#ManyToOne
#JoinColumn(name = "id_Order", referencedColumnName = "id_Order", nullable = false)
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
#ManyToOne
#JoinColumn(name = "id_Preorder", referencedColumnName = "id_Preorder", nullable = false)
public Preorder getPreorder() {
return preorder;
}
public void setPreorder(Preorder preorder) {
this.preorder = preorder;
}
#ManyToOne
#JoinColumn(name = "id_Unit_Type", referencedColumnName = "id_Unit_Type")
public UnitType getUnitType() {
return unitType;
}
public void setUnitType(UnitType unitType) {
this.unitType = unitType;
}
#ManyToOne
#JoinColumn(name = "id_Sale", referencedColumnName = "id_Sale")
public Sale getSale() {
return sale;
}
public void setSale(Sale sale) {
this.sale = sale;
}
#ManyToOne
#JoinColumn(name = "id_Delivery_Status_Type", referencedColumnName = "id_Delivery_Status_Type")
public DeliveryStatusType getDeliveryStatusType() {
return deliveryStatusType;
}
public void setDeliveryStatusType(DeliveryStatusType deliveryStatusType) {
this.deliveryStatusType = deliveryStatusType;
}
}
When I write a request manually, like this :
#Query("SELECT o FROM OrderDetail o WHERE o.idOrderDetail = :idOrderDetail")
public OrderDetail findOneCustom(#Param("idOrderDetail") Integer idOrderDetail);
That works, but that's ugly so I would prefer to use JpaRepository native function findOne()
After all investigation, I have found an interesting answer that is worked for me. I think it is all about defining column type on Db. For my case, I have defined the variable (rid as column) as varchar2(18) that was RID CHAR(18 BYTE).
Java part:
if (dhFlightRepo.findOneFlight(dhFlight.getRid())== null) {
dhFlightRepo.save(dhFlight);
}
If your value that you used as a parameter for findOne() is smallest than set value on column (18 for my case),the jpa doesn't accept value and returns null.You have to change column type as varchar2(18) it can be changeable according to given value on findOne() and work perfect.
I hope that works for all of you.I kindly request to give more detail If someone knows the reason with more detail.

Many to Many relation with extra column query Hibernate

I did a #ManyToMany relationship in Hibernate with an extra column successfully, as follows.
Activity.class
#Entity
#Table(name = "Activity")
public class Activity {
#Id
#GeneratedValue
private int actId;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.activity",
cascade = { CascadeType.ALL, CascadeType.REMOVE })
private Set<ActivityRepairMap> activityRepairMaps = new HashSet<ActivityRepairMap>();
#NotEmpty
private String actTurno;
#NotEmpty
private String actTexto;
private String actFhc;
public Activity() {
}
// Getters and Setters
}
Repair.class
#Entity
#Table(name = "Repair2")
public class Repair {
#Id
#GeneratedValue
private int repId;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.repair")
private Set<ActivityRepairMap> activityRepairMaps = new HashSet<ActivityRepairMap>();
#NotEmpty(message=Constants.EMPTY_FIELD)
private String repNombre;
private Integer repCant;
public Repair() {
}
// Getters and Setters
}
ActivityRepairMap.class
#Entity
#Table(name="ActivityRepairMap")
#AssociationOverrides({
#AssociationOverride(name="pk.activity", joinColumns=#JoinColumn(name="actId")),
#AssociationOverride(name="pk.repair", joinColumns=#JoinColumn(name="repId"))
})
public class ActivityRepairMap {
private ActivityRepairId pk = new ActivityRepairId();
private Integer actRepCant;
#EmbeddedId
public ActivityRepairId getPk() {
return pk;
}
public void setPk(ActivityRepairId pk) {
this.pk = pk;
}
#Transient
public Activity getActivity() {
return getPk().getActivity();
}
public void setActivity(Activity activity) {
getPk().setActivity(activity);
}
#Transient
public Repair getRepair() {
return getPk().getRepair();
}
public void setRepair(Repair repair) {
getPk().setRepair(repair);
}
#Column(name="actRepCant")
public Integer getActRepCant() {
return actRepCant;
}
public void setActRepCant(Integer actRepCant) {
this.actRepCant = actRepCant;
}
public ActivityRepairMap (){
}
// hashCode and equals methods
}
ActivityRepairId
#Embeddable
public class ActivityRepairId implements Serializable{
private static final long serialVersionUID = -776429030880521951L;
private Activity activity;
private Repair repair;
#ManyToOne
public Activity getActivity() {
return activity;
}
public void setActivity(Activity activity) {
this.activity = activity;
}
#ManyToOne
public Repair getRepair() {
return repair;
}
public void setRepair(Repair repair) {
this.repair = repair;
}
// hashCode and equals method
}
My problem is that I can't query all the repairs used in a specific activity.
I've already checked in MySQL Workbench that the data stored in the DB is correct.
I would appreciate if anyone can explain me either using HQL or Criteria how can I achieve this.
Thanks a lot.
In SQL this should be:
SELECT
r.*
FROM
repair r
LEFT JOIN
activity_repair ar
ON
ar.repair_id = r.id
WHERE
ar.activity_id = ?
Now it's still possible that a single activity is connected with two repairs, and though you might get some repairs twice in the result list. You could simple use a SELECT DISTINCT r.* to work around this, or work with a subquery.
In JPQL the query should be bascially the same as the SQL from above.
SELECT
r
FROM
Repair r
WHERE
r.activityRepairMaps.pk.activity = ?
If you need a JOIN:
SELECT
r
FROM
Repair r
JOIN
ActivityRepairMap arm
WHERE
arm.pk.activity = ?
Maybe you need to use #MapsId within your ActivityRepairMaps class. (I haven't done JPQL for a while now)
As a far as I remember, you should NOT use Entities within your #EmbeddedId classes, but instead use the raw #Id type of the corresponding classes. Instead of Repair and Activity, you should use int and int.

Hibernate #ManyToMany inserts 0 on the owning side of an insert

Having a bit of bother trying to figure out how to get my #ManyToMany mapping working in Hibernate in Dropwizard (using dropwizard-hibernate 6.2). I've tried several of the online examples. I'm trying to persist a twitter stream with user_mentions saved in a Targets table which is m2m with the Tweets table. So far all my attempts have been with an existing Target and a new Tweet (and due to my business rules, that will always be the case). I'll show code momentarily, but the consistent problem I'm having is that that the tweets_targets table winds up in all cases with the target_id set to the correct value, but the tweet_id set to 0.
Code is based on an article here: http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/
// Target class
#Entity
#Table(name="targets")
public class Target {
private long id;
private List<Tweet> tweets = new ArrayList<Tweet>();
#Id
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#ManyToMany(mappedBy="targets",targetEntity=Tweet.class)
public List<Tweet> getTweets() {
return tweets;
}
public void setTweets(List<Tweet> tweets) {
this.tweets = tweets;
}
}
// Tweet class
#Entity
#Table(name="tweets")
public class Tweet {
private long id;
private List<Target> targets = new ArrayList<Target>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "targets_tweets", joinColumns = {
#JoinColumn(name = "tweet_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "target_id", nullable = false, updatable = false)
})
public List<Target> getTargets() {
return this.targets;
}
public void setTargets(List<Target> targets) {
this.targets = targets;
for(Target t: targets){
t.getTweets().add(this);
}
}
}
The actual saving of a new Tweet is done in the DAO class which inherits from AbstractDAO in DropWizard. Relevant code is:
public long create(Tweet tweet) {
tweet.setTargets(getTargets(tweet));
return persist(tweet).getId();
}
#SuppressWarnings("unchecked")
private List<Target> getTargets(Tweet tweet) {
String[] mentions = tweet.getUserMentions().split(",");
return namedQuery(Target.FIND_BY_HANDLE)
.setParameterList("handles", mentions).list();
}
My named query just returns a list of all my targets based on their twitter handle as reported by the streams API.
Found the answer, hopefully this will help someone else.
The Id's in my DB are autoincrementing (I know, there's all kinds of debate on that, but it's what I have to work with), so once I added the annotation #GeneratedValue(strategy=GenerationType.IDENTITY) to the Tweet's Id property, everything started working.

JPA persist entities with one to many relation

Config
EcliplseLink 2.3.2
JPA 2.0
The entities are auto created from the db schema from netbeans with Entity Classes from Database... wizard.
The controller classes are auto created from netbeans with JPA Controller Classes from Entity Classes... wizard
Short version of question
In a classic scenario, two tables with one to many relation. I create the parent entity, then the child entity and I attach the child to the parent's collection. When I create (controller method) the parent entity, I expect the child entity to be created to and associated with parent. Why doesn't it happen?
Long version
Parent class
#Entity
#XmlRootElement
public class Device implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
private Integer id;
#Column(unique=true)
private String name;
#Temporal(TemporalType.TIMESTAMP)
private Date updated;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "deviceId")
private Collection<NetworkInterface> networkInterfaceCollection;
public Device() {
}
public Device(String name) {
this.name = name;
updated = new Date();
}
// setters and getters...
#XmlTransient
public Collection<NetworkInterface> getNetworkInterfaceCollection() {
return networkInterfaceCollection;
}
public void setNetworkInterfaceCollection(Collection<NetworkInterface> networkInterfaceCollection) {
this.networkInterfaceCollection = networkInterfaceCollection;
}
public void addNetworkInterface(NetworkInterface net) {
this.networkInterfaceCollection.add(net);
}
public void removeNetworkInterface(NetworkInterface net) {
this.networkInterfaceCollection.remove(net);
}
// other methods
}
Child class
#Entity
#Table(name = "NETWORK_INTERFACE")
#XmlRootElement
public class NetworkInterface implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
private Integer id;
private String name;
#Temporal(TemporalType.TIMESTAMP)
private Date updated;
#JoinColumn(name = "DEVICE_ID", referencedColumnName = "ID")
#ManyToOne(optional = false)
private Device deviceId;
public NetworkInterface() {
}
public NetworkInterface(String name) {
this.name = name;
this.updated = new Date();
}
// setter and getter methods...
public Device getDeviceId() {
return deviceId;
}
public void setDeviceId(Device deviceId) {
this.deviceId = deviceId;
}
}
Main class
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("wifi-dbPU");
DeviceJpaController deviceController = new DeviceJpaController(emf);
NetworkInterfaceJpaController netController = new NetworkInterfaceJpaController(emf);
Device device = new Device("laptop");
NetworkInterface net = new NetworkInterface("eth0");
device.getNetworkInterfaceCollection().add(net);
deviceController.create(device);
}
}
This class throws a NullPointerException in line: device.getNetworkInterfaceCollection().add(net);
The system knows that there is a new entity device and it has an element net in it's collection. I expected it to write device in db, get device's id, attach it to net and write it in db.
Instead of this, I found that these are the steps I have to do:
deviceController.create(device);
net.setDeviceId(device);
device.getNetworkInterfaceCollection().add(net);
netController.create(net);
Why do I have to create the child when the parent class knows it's child and it should create it for me?
The create method from DeviceJpaController (sorry for the long names in fields, they are auto generated).
public EntityManager getEntityManager() {
return emf.createEntityManager();
}
public void create(Device device) {
if (device.getNetworkInterfaceCollection() == null) {
device.setNetworkInterfaceCollection(new ArrayList<NetworkInterface>());
}
EntityManager em = null;
try {
em = getEntityManager();
em.getTransaction().begin();
Collection<NetworkInterface> attachedNetworkInterfaceCollection = new ArrayList<NetworkInterface>();
for (NetworkInterface networkInterfaceCollectionNetworkInterfaceToAttach : device.getNetworkInterfaceCollection()) {
networkInterfaceCollectionNetworkInterfaceToAttach = em.getReference(networkInterfaceCollectionNetworkInterfaceToAttach.getClass(), networkInterfaceCollectionNetworkInterfaceToAttach.getId());
attachedNetworkInterfaceCollection.add(networkInterfaceCollectionNetworkInterfaceToAttach);
}
device.setNetworkInterfaceCollection(attachedNetworkInterfaceCollection);
em.persist(device);
for (NetworkInterface networkInterfaceCollectionNetworkInterface : device.getNetworkInterfaceCollection()) {
Device oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface = networkInterfaceCollectionNetworkInterface.getDeviceId();
networkInterfaceCollectionNetworkInterface.setDeviceId(device);
networkInterfaceCollectionNetworkInterface = em.merge(networkInterfaceCollectionNetworkInterface);
if (oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface != null) {
oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface.getNetworkInterfaceCollection().remove(networkInterfaceCollectionNetworkInterface);
oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface = em.merge(oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface);
}
}
em.getTransaction().commit();
} finally {
if (em != null) {
em.close();
}
}
}
I finally understood the logic behind persisting one to many entities. The process is:
Create parent class
Persist it
Create child class
Associate child with parent
Persist child (the parent collection is updated)
With code:
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("wifi-dbPU");
DeviceJpaController deviceController = new DeviceJpaController(emf);
NetworkInterfaceJpaController netController = new NetworkInterfaceJpaController(emf);
Device device = new Device("laptop"); // 1
deviceController.create(device); // 2
NetworkInterface net = new NetworkInterface("eth0"); // 3
net.setDeviceId(device.getId()); // 4
netController.create(net); // 5
// The parent collection is updated by the above create
}
}
Now, I can find a device (with id for example) and I can get all its children using
Collection<NetworkInterface> netCollection = device.getNetworkInterfaceCollection()
In the device entity class, which I posted in the question, there is no need for the methods addNetworkInterface and removeNetwokrInterface.
#Dima K is correct in what they say. When you do this:
Device device = new Device("laptop");
NetworkInterface net = new NetworkInterface("eth0");
device.getNetworkInterfaceCollection().add(net);
deviceController.create(device);
The collection in device hasn't been initialized and so you get a NPE when trying to add to it. In your Device class, when declaring your Collection, you can also initialize it:
private Collection<NetworkInterface> networkInterfaceCollection = new CollectionType<>();
As for persisting, your assumptions are correct but I think the execution is wrong. When you create your device, make it persistent with JPA right away (doing transaction management wherever needed).
Device device = new Device("laptop");
getEntityManager().persist(device);
Do the same for the NetworkInterface:
NetworkInterface net = new NetworkInterface("eth0");
getEntityManager().persist(net);
Now since both your entities are persisted, you can add one to the other.
device.getNetworkInterfaceCollection().add(net);
JPA should take care of the rest without you having to call any other persists.
This is a known behavior of collection data members.
The easiest solution is to modify your collection getter to lazily create the collection.
#XmlTransient
public Collection<NetworkInterface> getNetworkInterfaceCollection() {
if (networkInterfaceCollection == null) {
networkInterfaceCollection = new Some_Collection_Type<NetworkInterface>();
}
return networkInterfaceCollection;
}
Also, remember to refer to this data member only through the getter method.
This exception means you're trying to locate an entity (probably by em.getReference()) that hasn't been persisted yet.
You cannot you em.getReference() or em.find() on entities which still don't have a PK.
In order to enable save ability on a #OneToMany relation e.g.
#OneToMany(mappedBy="myTable", cascade=CascadeType.ALL)
private List<item> items;
Then you have to tell to your #ManyToOne relation that it is allowed to update myTable like this updatable = true
#ManyToOne #JoinColumn(name="fk_myTable", nullable = false, updatable = true, insertable = true)

Categories