I am trying to implement a ternary relation with the entities Customer, CustomerServant and Room. The teranry relation also has the additional attributes StartDate, EndDate and also an Id. Therefore I created this class to get the relation table:
#Entity(name = "bookings")
#Table(name = "bookings")
public class Bookings {
#Id
#SequenceGenerator(
name = "bookings_sequence",
sequenceName = "bookings_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = SEQUENCE,
generator = "bookings_sequence"
)
#Column(
name = "bookings_id",
updatable = false
)
private Long Id;
#Column(
name = "Travel_Start_Date",
nullable = false
)
private String travelStartDate ;
#Column(
name = "Travel_End_Date",
nullable = false
)
private String travelEndDate;
#ManyToOne ( cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "customer", insertable = false, updatable = false)
private Customer customer = new Customer();
#ManyToOne
#JoinColumn(name = "room", insertable = false, updatable = false)
private Room room;
#ManyToOne
#JoinColumn(name = "customerServant", insertable = false, updatable = false)
private CustomerServant customerServant;
What I am struggling with is what I should write in the other three classes which are referencing each other.
I do understand that to create a single m-to-m relation with additional relational attributes I can implement a class as I did above and simply write: e.g.
class Bookings {
int Id;
Date StartDate;
Date EndDate;
#ManyToOne
#JoinColumn(name = "customer_id")
Customer customer;
#ManyToOne
#JoinColumn(name = "Room")
Room room;
}
class Customer {
//...
#OneToMany(mappedBy = "customer")
Set<Bookings> bookings;
}
class Room {
#OneToMany(mappedBy = "room")
Set<Bookings> bookings;
but what do I do when I want a ternary relation?
This is how I solved it using the info provided in the article sent by #warmCabin (thorben-janssen.com/ternary-associations):
Using an association table class like above with the attributes startDate, endDate, booking_id and the references to the three entities Room, Customer, CustomerServant, provided that theres an according #ManyToOne annotation for the three referencing entities, just like in the question above.
e.g. like above
#ManyToOne (cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "customer", insertable = false, updatable = false)
private Customer customer = new Customer();
Then I added this to all entities that are connected via the ternary relation:
#OneToMany(mappedBy = "<enter varName from ternary relation table>")
Set<T> var = new HashSet<>();
With this line of code, when a relation is stored e.g. a booking like in my case, this relation gets stored in every participating entity and every stored relation gets the Ids of all participating entities.
In my case it would look like this, for the three entities:
#Entity(name="Room")
#Table(name="Room")
class Room {
//attributes...
#OneToMany(mappedBy = "room")
Set<Bookings> bookings = new HashSet<>();
//...
}
#Entity(name="Customer")
#Table(name="Customer")
class Customer {
//attributes...
#OneToMany(mappedBy = "customer")
Set<Bookings> bookings = new HashSet<>();
//...
}
#Entity(name="CustomerServant")
#Table(name="CustomerServant")
class CustomerServant {
//attributes...
#OneToMany(mappedBy = "customerServant")
Set<Bookings> bookings = new HashSet<>();
//...
}
In the article cited above it is said that its also possible to create such a relation with a HashMap, but because the first appraoch, with an own entity class as a ternary relation, works for me, I didnt check if it would also work in my case. If necessary check the article thorben-janssen.com/ternary-associations.
Related
When saving my entities, child entities that work through the #OneToMany relationship are not saved to their tables. I can’t understand what’s the matter.
Employee:
#Entity
#Table(name = "EMPLOYEE", schema = PUBLIC)
public class Employee {
private String name;
private String lastname;
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
List<EmployeePhoneNumber> employeePhoneNumbers = new ArrayList<>();
}
EmployeePhoneNumber:
#Entity
#Table(name = "EMPLOYEE_PHONES", schema = PUBLIC)
public class EmployeePhoneNumber {
#Id
#SequenceGenerator(allocationSize = 1, name = "SEQ_EMPLOYEE_PHONES", schema = PUBLIC,
sequenceName = "EMPLOYEE_PHONES_ID_SEQ")
#GeneratedValue(generator = "SEQ_EMPLOYEE_PHONES", strategy = GenerationType.SEQUENCE)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#ManyToOne
#JoinColumn(name = "employee_id", referencedColumnName = "id",
nullable = false, insertable = false, updatable = false)
private Employee employee;
#Column(name = "PHONE_NUMBER", unique = true, nullable = false)
private String phoneNumber;
#Enumerated(EnumType.STRING)
#Column(name = "NUMBER_TYPE", nullable = false)
private PhoneNumberType phoneNumberType;
}
How I set those fields and then save the entity:
EmployeePhoneNumber workPhone = new EmployeePhoneNumber();
workPhone.setPhoneNumber(workPhone);
workPhone.setPhoneNumberType(PhoneNumberType.WORK_PHONE);
EmployeePhoneNumber mobilePhone = new EmployeePhoneNumber();
mobilePhone.setPhoneNumber(mobilePhone);
mobilePhone.setPhoneNumberType(PhoneNumberType.MOBILE_PHONE);
EmployeePhoneNumber corporatePhone = new EmployeePhoneNumber();
corporatePhone.setPhoneNumber(corporatePhoneNumber);
corporatePhone.setPhoneNumberType(PhoneNumberType.CORPORATE_PHONE);
List<EmployeePhoneNumber> employeePhoneNumbers = employee.getEmployeePhoneNumbers();
employeePhoneNumbers.add(workPhone);
employeePhoneNumbers.add(mobilePhone);
employeePhoneNumbers.add(corporatePhone);
employee.setEmployeePhoneNumbers(employeePhoneNumbers);
employeeRepository.save(employee);
Upon completion of the method, I do not have a single error, everything works out correctly, only the tables are not filled - why?
You must also set the Employee reference in EmployeePhoneNumber because Hibernate will use this to save it.
workPhone.setEmployee(employee);
mobilePhone.setEmployee(employee);
corporatePhone.setEmployee(employee);
The best solution would be to create an addEmployeePhoneNumber method on the Employee like this:
public void addEmployeePhoneNumber(EmployeePhoneNumber phoneNumber) {
phoneNumber.setEmployee(this);
employeePhoneNumbers.add(phoneNumber);
}
That way you will not forget to set both sides of the relationship.
org.springframework.dao.InvalidDataAccessApiUsageException:
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously
fetch multiple bags: [Order.items, OrderItem.options];
Above is an exception i faced when i join three tables like below.
OrderItemOption.java
#Entity
public class OrderItemOption {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_option_id")
private Long id;
#Column(name = "item_id", nullable = false)
private Long itemId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "item_id",
referencedColumnName = "item_id",
insertable = false,
updatable = false
)
private OrderItem orderItem;
}
OrderItem.java
#Entity
public class OrderItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private Long id;
#Column(name = "order_id", nullable = false)
private Long orderId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "order_id",
referencedColumnName = "order_id",
insertable = false,
updatable = false,
nullable = false
)
private Order order;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "orderItem")
#OrderBy("item_option_id ASC")
private List<OrderItemOption> options;
}
Order.java
#Entity
public class Order {
#Id
#Column(name = "order_id", nullable = false)
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
#OrderBy("item_id ASC")
private List<OrderItem> items;
}
And here's my QueryDSL code to join them at one time.
final QOrder order = QOrder.order;
final QOrderItem item = QOrderItem.orderItem;
final QOrderItemOption option = QOrderItemOption.orderItemOption;
from(order)
.leftJoin(order.items, item).fetchJoin()
.leftJoin(item.options, option).fetchJoin()
.where(
order.id.eq(orderId)
.and(item.id.in(itemIds))
.and(option.id.in(optionIds))
)
.fetchOne())
What i'm trying to do is to get Order object which contains filtered relationship, so that i can access filtered children via order object.
and the type of relationship should be a List, not a Set.
for example, order.getItems().get(0).getOptions.get(0)
How can i achieve that goal?
To avoid above exception there are two possibilities:
Change List to Set
or
Use List but do not fetch two bags. This means don't use fetchJoin() on both collections.
Filtering:
Using where conditions collections will be not filtered. Collections will contain all associated objects. Joining in JPA is for creating conditions on root object - Order. It is not the same as in SQL.
It is possible to filter associated collections using JPA 2.1 JOIN ON feature. This allows additional conditions in ON clause
see for example QueryDSL Left Join with additional conditions in ON
If you really can not use Set instead of List:
Parent.class
#OneToMany(
mappedBy = "parent",
orphanRemoval = true,
cascade = { CascadeType.PERSIST, CascadeType.MERGE }
)
#OrderColumn(name = "position")
private List<Child> childs = new ArrayList<>();
Child.class
#ManyToOne(fetch = FetchType.LAZY)
private Parent parent;
And create a column in the Child's table named e.g "position"
ALTER TABLE child ADD COLUMN position integer NOT NULL default 0
And if you can not use other column in table, them you need to query the lists in sequence. Or use the id of the child and a custom getter.
#OrderColumn(name = "id_child", updatable = false, insertable = false)
public List<Child> getChilds() {
childs.removeAll(Collections.singleton(null));
return childs;
}
Here's what I'm trying to do...I have a Person
#Entity
#Table(name = "PERSON",
uniqueConstraints = {
#UniqueConstraint(columnNames = {"SSN"})
}
)
#DynamicInsert(true)
#DynamicUpdate(true)
#SelectBeforeUpdate(true)
public class Person implements java.io.Serializable {
private static final long serialVersionUID = 6732775093033061190L;
#Version
#Column(name = "OBJ_VERSION")
private Timestamp version;
#Id
#Column(name = "SSN", length = 12, nullable = false, insertable = true, updatable = true)
private String ssn;
#Column(name = "LAST_NAME", length = 50, nullable = false, insertable = true, updatable = true)
private String lastName;
#Column(name = "FIRST_NAME", length = 30, nullable = false, insertable = true, updatable = true)
private String firstName;
#Column(name = "MIDDLE_NAME", length = 30, nullable = true, insertable = true, updatable = true)
private String middleName;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "person", cascade = CascadeType.ALL)
private Passport passport;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Citizenship> citizenship = new HashSet<>();
// Getters and setters left out for brevity
and each person can have one Passport
#Entity
#Table(name = "PASSPORT",
uniqueConstraints = {
#UniqueConstraint(columnNames = {"SSN", "PASSPORT_NUMBER"})
}
)
#DynamicInsert(true)
#DynamicUpdate(true)
#SelectBeforeUpdate(true)
public class Passport implements java.io.Serializable {
private static final long serialVersionUID = 6732775093033061190L;
#Version
#Column(name = "OBJ_VERSION")
private Timestamp version;
#Id
#Column(name = "SSN", length = 12, nullable = false, insertable = true, updatable = true)
private String ssn;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SSN")
#MapsId
private Person person;
#Column(name = "EXPIRATION_DATE", nullable = true, insertable = true, updatable = false)
private GregorianCalendar expirationDate;
#Column(name = "ISSUING_COUNTRY", nullable = true, insertable = true, updatable = false)
private String issuingCountry;
#Column(name = "PASSPORT_NUMBER", nullable = false, insertable = true, updatable = false)
private String passportNumber;
// Getters and setters left out for brevity
This works, each person can have one Passport and the Passport.ssn is assigned the value of the Person.ssn. This is being done because SSN is a unique identifier and it avoids the need for link tables.
Each person can also have a Citizenship
#Entity
#Table(name = "CITIZENSHIP")
#DynamicInsert(true)
#DynamicUpdate(true)
#SelectBeforeUpdate(true)
public class Citizenship implements java.io.Serializable {
private static final long serialVersionUID = 6732775093033061190L;
#Version
#Column(name = "OBJ_VERSION")
private Timestamp version;
#EmbeddedId
private CitizenshipId citizenshipId;
#Column(name = "DATE_OF_CITIZENSHIP")
private GregorianCalendar dateOfCitizenship;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "SSN")
#MapsId("ssn")
private Person person;
// Getters and setters left out for brevity
I have successfully added a person with a passport and a person without a passport. I have added a third person with a passport and dual citizenship with
// This person has a passport and is a dual citizen.
person = new Person();
person.setSsn("654-89-7531");
person.setFirstName("Lois");
person.setLastName("Lane");
passport = new Passport();
passport.setExpirationDate(new GregorianCalendar());
passport.setIssuingCountry("USA");
passport.setPassportNumber("987654");
Set<Citizenship> citizenshipSet = new HashSet<>();
CitizenshipId citizenshipId = new CitizenshipId();
citizenshipId.setCountry("USA");
Citizenship c = new Citizenship();
c.setDateOfCitizenship(new GregorianCalendar());
c.setCitizenshipId(citizenshipId);
c.setPerson(person);
citizenshipSet.add(c);
citizenshipId = new CitizenshipId();
citizenshipId.setCountry("CAN");
c = new Citizenship();
c.setDateOfCitizenship(new GregorianCalendar());
c.setCitizenshipId(citizenshipId);
c.setPerson(person);
citizenshipSet.add(c);
person.setPassport(passport);
passport.setPerson(person);
session.saveOrUpdate(person);
for(Citizenship citizen : citizenshipSet) {
session.saveOrUpdate(citizen);
}
session.flush();
session.clear();
This looks weird/inefficient to me, but it does work (tips for improvement would be appreciated). But as desired, the Person.ssn is carried into the Citizenship. Here's the problem:
The Person with dual Citizenship currently has citizenship in USA and Canada. Let's assume this is wrong and the Person has citizenship in USA and Mexico, which means the CitizenshipId.country needs to change from "CAN" to "MEX". I have tried a bunch of variations of code like
Criteria citCriteria = session.createCriteria(Citizenship.class);
citCriteria.add(Restrictions.eq("citizenshipId.ssn", "654-89-7531"));
List<Citizenship> citizenship = citCriteria.list();
for(Citizenship c : citizenship) {
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
session.evict(c);
c.getCitizenshipId().setCountry("MEX");
session.saveOrUpdate(c);
session.flush();
session.clear();
}
}
With "show_sql" on, this doesn't perform an update, even though I can see the values change when debugging. I did try an evict(), then set the country, then saveOrUpdate, which made a new entry (I figured it would).
Phew...the question is: How can the values in an Embeddable class be updated when that class is being used as an EmbeddedId? I feel like I'm close but just missing one thing...
Thanks.
Adding CitizenshipID for reference
#Embeddable
public class CitizenshipId implements Serializable {
private static final long serialVersionUID = 6732775093033061190L;
String ssn;
String country;
// Omitted getters, setters, constructors, hashcode, and equals
Have you tried:
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
session.evict(c);
c.getCitizenshipId().setCountry("MEX");
c.getPerson().getCitizenship().add(c); // TRY ADDING THIS
session.saveOrUpdate(c);
session.flush();
session.clear();
}
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
// TRY ADDING THIS ------------------------
//session.evict(c);
CitizenshipId cid = new CitizenshipId();
cid.setSsn(c.getCitizenshipId().getSsn();
cid.setCountry("MEX");
c.setCitizenshipId(cid); // references new CID -- should issue update
// -----------------------------------------
session.saveOrUpdate(c);
session.flush();
session.clear();
}
I removed the .evict due to the description in the API:
Remove this instance from the session cache. Changes to the instance
will not be synchronized with the database. This operation cascades to
associated instances if the association is mapped with
cascade="evict".
Topic How to update values associated with Primary Key in Spring-JPA is inline with what Dan posted above about creating a new object with the old object Id. However, topic Hibernate - update the primary key 'id' column in the table does state that Hibernate doesn't allow updates to primary keys.
The objective here was to create a Person with a(n) SSN, possibly with a Passport, and a Citizenship. SSN is intended to be the primary key so I mapped Person to Passport and Citizenship and used SSN as the JoinColumn.
Person to Passport is a one to one relationship, so that wasn't a problem.
Person to Citizenship is a one to many relationship. This relationship means I had to create an embeddable ID. To make each Citizenship unique the embeddable class CitizenshipId was created with SSN and Country.
Using the accepted answer for Hibernate - update the primary key 'id' column in the table I changed the variations of
Criteria citCriteria = session.createCriteria(Citizenship.class);
citCriteria.add(Restrictions.eq("citizenshipId.ssn", "654-89-7531"));
List<Citizenship> citizenship = citCriteria.list();
for(Citizenship c : citizenship) {
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
session.evict(c);
c.getCitizenshipId().setCountry("MEX");
session.saveOrUpdate(c);
session.flush();
session.clear();
}
}
to
Query query=session.createQuery("update Citizenship set country = :country1 where ssn = :ssn and country = :country2")
.setString("country1", "MEX").setString("ssn", "654-89-7531").setString("country2", "CAN");
query.executeUpdate();
And an update did occur. Being unable to make an update via typical code (use criteria to get data, update it, then call saveOrUpdate) but being able to make an update via a query doesn't make a whole lot of sense to me. I know that key management is more times than not best left to the database, but when a unique value such as SSN is being used there is no need for another key. If an ID is identified within the code without a generation strategy it stands to reason that the IDs can be updated...JMHO.
Thanks to Dan for his ideas. I hope this topic and its references helps others.
I'm looking for a way to implement (0..n) many to many relation in JPA, much possibly using #ManyToMany annotation. All examples that I found were about (1..n) relations. What I need to accomplish:
- I've got two entities: Contact and Tag. Each Contact can have 0..n Tags. Each Tag can have 0..n Contacts. From SQL point of view it would look like
this: Contact (0..n) --- (1) Contact_has_Tag (1) --- (0..n) Tag.
Code below is not working for me because JPA is linking columns with INNER JOIN.
OFC I could do this using intermediate entity and #OneToMany and #ManyToOne annotations, but I want a simpler sollution.
#Data
#Entity
public class Contact {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private long id;
#Column(unique = true)
private String email;
// ...
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonIgnore
#JoinTable(
name = "contact_has_tag",
joinColumns = #JoinColumn(name = "contact_id", referencedColumnName = "id", updatable = false, nullable = true),
inverseJoinColumns = #JoinColumn(name = "tag_id", referencedColumnName = "id", updatable = false, nullable = true))
private List<ContactTag> contactTags = new ArrayList<ContactTag>();
}
#Entity
#Data
public class ContactTag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column
private String name;
#ManyToMany(mappedBy="contactTags", fetch = FetchType.LAZY)
#JsonIgnore
private List<Contact> contacts = new ArrayList<Contact>();
}
Any ideas how it should be done?
Scenario:
I have a products table with these fields: id, code, description, photo.
I have a product_stock view from other schema with fields: prod_code, stok_tot
Misson: What I need to do in Spring Boot is to join the products with their total stock.
Rules:
I can't use #Query on this task, I need to join them at the Product Entity Class, so that stock became some kind of Transient field of product.
I can't change the fact that product's ID is a Long and ProductStock's ID is a String, but I could use product's code field instead right? (how?)
So far... I tryed to use #OneToOne and #JoinColumn to do the job, but my REST gives me the stock field as NULL.
"Estoque.java"
#Entity
#Table(name = "VW_ESTOQUE", schema = "ASICAT")
public class Estoque {
#Id
#Column(name = "CD_BEM_SERVICO", unique = true, nullable = false)
private String codigo;
#Column(name = "ESTOQUE")
private Long estoque;
// getters and setters hidden by me
}
"Produto.java"
#Entity
#NamedEntityGraph(name = "Produto.detail", attributeNodes = #NamedAttributeNode("categorias"))
public class Produto implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String codigo;
private String descricao;
// here is where I get the null values
#Transient
#OneToOne(fetch = FetchType.LAZY)
#JoinTable(name = "VW_ESTOQUE", joinColumns = #JoinColumn(name = "CODIGO", referencedColumnName = "CODIGO"), inverseJoinColumns = #JoinColumn(name = "CD_BEM_SERVICO", referencedColumnName = "CODIGO"))
private Estoque estoque;
private String hash;
#ManyToMany(mappedBy = "produtos", fetch = FetchType.EAGER)
#BatchSize(size = 10)
private List<Categoria> categorias = new ArrayList<>();
// getters and setters hidden by me
}
In my product repository I call FindAll()
You have annotated Produto.estoque as #Transient, which means that it is not part of the persistent state of the entity. Such a field will be neither written nor read when instances of that entity are managed. That's not going to serve your purpose.
There are two things I can imagine you might have been trying to achieve:
That every time an Estoque is accessed via a Produto, it should be loaded from the DB to ensure its freshness. JPA does not provide for that, though you might want to annotate Estoque with #Cacheable(value = false), and specify the lazy fetch strategy on the Produto side of the relationship.
You want to avoid the persistence provider attempting to persist any changes to an Estoque, since it is backed by a view, not an updatable table. This we can address.
My first suggestion would be to map ASICAT.VW_ESTOQUE as a secondary table instead of an entirely separate entity. That might look something like this:
#Entity
#SecondaryTable(name = "VW_ESTOQUE", schema = "ASICAT"
pkJoinColumns = {
#PrimaryKeyJoinColumn(name = "CD_BEM_SERVICO",
referencedColumnName = "CODIGO") })
public class Produto implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String codigo;
private String descricao;
#Column(name = "ESTOQUE", table = "VW_ESTOQUE", nullable = true,
insertable = false, updatable = false)
private Long estoque;
// ...
}
You might furthermore avoid providing a setter for the estoque property.
But the SecondaryTable approach might not work well if you cannot rely on the ESTOQUE view always to provide a row for every row of PRODUTO, as there will very likely be an inner join involved in retrievals. Moreover, you don't get lazy fetches this way. The main alternative is more or less what you present in your question: to set up a separate Estoque entity.
If you do set up a separate Estoque, however, then I would approach it a bit differently. Specifically,
I would make the relationship bidirectional, so that I could
make the Estoque entity the relationship owner.
Something like this, then:
#Entity
public class Produto implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String codigo;
private String descricao;
// must not be #Transient:
#OneToOne(fetch = FetchType.LAZY, mappedBy = "produto", cascade = {
CascadeType.REFRESH
})
private Estoque estoque;
// ...
}
#Entity
#Table(name = "VW_ESTOQUE", schema = "ASICAT")
#Cacheable(value = false)
public class Estoque {
#Id
#Column(name = "CD_BEM_SERVICO", nullable = false,
insertable = false, updatable = false)
private String codigo;
#Column(name = "ESTOQUE", insertable = false, updatable = false)
private Long estoque;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "CD_BEM_SERVICO", referencedColumnName = "CODIGO",
nullable = false, insertable = false, updatable = false, unique = true)
Produto produto;
// getters and setters hidden by me
}
In this case, I would avoid providing setter methods for any of the properties of Estoque, and avoid providing any constructor that allows initial property values to be set. Thus, to a first approximation, the only
way an instance's properties will take non-null values is if they are set by the persistence provider.
Additionally, since you mention Oracle, if you are using TopLink as your persistence provider then you might consider applying its #ReadOnly extension attribute to the Estoque entity, in place of or even in addition to some of these protections against trying to insert into or update the view.