JPA not insert the data in the row after persist - jpa

I am using an ID generated value in my entity
#Id
#TableGenerator(
name="marcaTable",
table="JPA_WXS_APP_SEQUENCE_GENERATOR",
pkColumnName="GEN_KEY",
valueColumnName="GEN_VALUE",
pkColumnValue="MARCA_ID",
allocationSize=1,
initialValue=0)
#GeneratedValue(strategy=GenerationType.TABLE,generator="marcaTable")
public int getId() {
return Id;
}
I use a table to save the id.
If I execute this code twice its fail because there are duplicates ID (1 id)
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("ACoches");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
marca nmarca2 = new marca();
nmarca2.setNombre_marca("pepito");
em.flush();
em.persist(nmarca2);
tx.commit();
em.close();
emf.close();
}
}
But if I manually execute a select of marca table it is empty, it seems that JPA dont insert the data in the row just when i make the em.persist(nmarca2);
If I delete the JPA_WXS_APP_SEQUENCE_GENERATOR table manually and I select again the marca table now yes I can see the register.
Thanks in advance!!!

persist() just registers the object to be persisted. It will not be inserted until commit() or flush(). If you call flush() after the persist() it will have been inserted.
Can't see why you would get a duplicate id. Turn logging on finest to see what SQL is being executed.
One issue may be your initialValue=0, try removing or changing it to 1.

Related

How is it possible to access pre-filtered dependent entities from Associations retrieved via JPA/Hibernate criteria query with restrictions?

I have a provider:user=1:N association modeled with entities, Hibernate/JPA.
Then I want to query a provider/user pair via restrictions on attributes of the dependent entity, like certain values for the attributes of the user, let’s say its id, date of birth, etc.
The logged sql has a proper join and all the attributes of the two entities in the select. I tried it manually, it returns the expected single row.
Thus, on entity level, I expect a single provider entity to be returned with the user list containing the queried user.
Indeed, the corresponding provider entity is returned, but when I then want to access the user via the provider’s user list, it hits the DB a second time and reads all users of the provider totally neglecting my restrictions of the query.
The observed behavior is the same for queries formulated with HQL, Hibernate Criteria (also with #Filter), JPA CriteriaBuilder.
What am I missing here?
Do those restrictions only affect the selection of the root entities (which is provider in my case)?
The problem is sketched on
https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html
Under 5.4 Associations it says:
The kittens collections held by the Cat instances returned by the previous two queries are not pre-filtered by the criteria. If you want to retrieve just the kittens that match the criteria, you must use a ResultTransformer.
Is this thus the intended behavior for this kind of API?
Or is there a convenient possibility to access just the restricted sub set of the dependent entities?
Regards,
Wolfgang
Here comes some source code to precise my verbal description above.
Provider and User table.
drop table EX_USER;
drop table EX_PROVIDER;
create table EX_PROVIDER
( id number(*,0) not null
,name varchar2(255) not null
,primary key (id)
);
insert into EX_PROVIDER (id,name) values (0 ,'Provider_A');
insert into EX_PROVIDER (id,name) values (1 ,'Provider_B');
commit;
create table EX_USER
( id number(*,0) not null
, ex_provider_id number(*,0) not null
,name varchar2(255)
,location varchar2(255)
,primary key (id)
,constraint ex_user_provider_fk foreign key(ex_provider_id) references EX_PROVIDER(id)
);
insert into EX_USER (id,ex_provider_id,name,location) values (0,0,'User_1','Munich');
insert into EX_USER (id,ex_provider_id,name,location) values (1,0,'User_2','Berlin');
insert into EX_USER (id,ex_provider_id,name,location) values (2,1,'User_3','Munich');
commit;
Entities generated with Eclipse "JPA Tools".
#Entity
#Table(name="EX_PROVIDER")
#NamedQuery(name="ExProvider.findAll", query="SELECT e FROM ExProvider e")
public class ExProvider implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="EX_PROVIDER_ID_GENERATOR", sequenceName="KONST_SD_SEQ")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="EX_PROVIDER_ID_GENERATOR")
private long id;
private String name;
//bi-directional many-to-one association to ExUser
#OneToMany(mappedBy="exProvider",fetch=FetchType.LAZY)
private Set<ExUser> exUsers=new HashSet<ExUser>();
public ExProvider() {
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Set<ExUser> getExUsers() {
return this.exUsers;
}
public void setExUsers(Set<ExUser> exUsers) {
this.exUsers = exUsers;
}
public ExUser addExUser(ExUser exUser) {
getExUsers().add(exUser);
exUser.setExProvider(this);
return exUser;
}
public ExUser removeExUser(ExUser exUser) {
getExUsers().remove(exUser);
exUser.setExProvider(null);
return exUser;
}
}
#Entity
#Table(name="EX_USER")
#NamedQuery(name="ExUser.findAll", query="SELECT e FROM ExUser e")
public class ExUser implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="EX_USER_ID_GENERATOR", sequenceName="KONST_SD_SEQ")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="EX_USER_ID_GENERATOR")
private long id;
private String location;
private String name;
//bi-directional many-to-one association to ExProvider
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="EX_PROVIDER_ID")
private ExProvider exProvider;
public ExUser() {
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getLocation() {
return this.location;
}
public void setLocation(String location) {
this.location = location;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public ExProvider getExProvider() {
return this.exProvider;
}
public void setExProvider(ExProvider exProvider) {
this.exProvider = exProvider;
}
}
My intention with the below code was to retrieve Provider 0 (Provider_A) containing User 1 (Berlin) in its user list.
#SuppressWarnings("unchecked")
private void demo() {
EntityManager em = null;
try {
em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
Session session = (Session) em.getDelegate();
Criteria crit = session.createCriteria(ExProvider.class, "provider")
.createCriteria("exUsers", "user")
.add(Restrictions.eq("user.location","Berlin"))
;
List<ExProvider> providerList=(List<ExProvider>)crit.list();
logExProviderList(providerList);
tx.commit();
} finally {
if (tx!=null && tx.isActive()) tx.rollback();
}
} finally {
if (em!=null) em.close();
}
}
private void logExProviderList(List<ExProvider> providerList) {
for (ExProvider provider: providerList) {
logger.info("Criteria: provider=["+provider.getId()+"]");
for (ExUser user : provider.getExUsers()) {
logger.info("Criteria: user=["+user.getId()+"] name=["+user.getName()+"] location=["+user.getLocation()+"]");
}
}
}
The 1st SQL is what I expected. It is executed when crit.list() is called. On SQL level it returns the expected single row for Provider 0, User 1.
SELECT this_.id AS id43_1_,
this_.name AS name43_1_,
user1_.id AS id44_0_,
user1_.EX_PROVIDER_ID AS EX4_44_0_,
user1_.location AS location44_0_,
user1_.name AS name44_0_
FROM EX_PROVIDER this_
INNER JOIN EX_USER user1_
ON this_.id=user1_.EX_PROVIDER_ID
WHERE user1_.location=?;
However, this is not mapped to entity level as I expected. There, the restriction seems to affect the selection of the Provider only. When the user list is accessed, all users of the provider are read from DB neglecting the 'Berlin' restriction.
This was the same whether I used HQL, Hibernate Criteria (also with #Filter), JPA CriteriaBuilder.
SELECT exusers0_.EX_PROVIDER_ID AS EX4_43_1_,
exusers0_.id AS id1_,
exusers0_.id AS id44_0_,
exusers0_.EX_PROVIDER_ID AS EX4_44_0_,
exusers0_.location AS location44_0_,
exusers0_.name AS name44_0_
FROM EX_USER exusers0_
WHERE exusers0_.EX_PROVIDER_ID=?;
The log.
Criteria: provider=[0]
Criteria: user=[1] name=[User_2] location=[Berlin]
Criteria: user=[0] name=[User_1] location=[Munich]
I may achieve the expected result set using setResultTransformer(), but in this case the properly selected result is returned as some rows of entities.
#SuppressWarnings("unchecked")
private void demo() {
EntityManager em = null;
try {
em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
Session session = (Session) em.getDelegate();
Criteria crit = session.createCriteria(ExProvider.class, "provider")
.createCriteria("exUsers", "user")
.add(Restrictions.eq("user.location","Berlin"))
.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
;
List<ExProvider> providerList=(List<ExProvider>)crit.list();
logExProviderMapList(providerList);
tx.commit();
} finally {
if (tx!=null && tx.isActive()) tx.rollback();
}
} finally {
if (em!=null) em.close();
}
}
private void logExProviderMapList(List providerList) {
Iterator iter = providerList.iterator();
while ( iter.hasNext() ) {
Map map = (Map) iter.next();
ExProvider provider = (ExProvider) map.get("provider");
if(provider!=null) {
logger.info("Criteria: provider=["+provider.getId()+"]");
}
ExUser user = (ExUser) map.get("user");
if(user!=null) {
logger.info("Criteria: user=["+user.getId()+"] name=["+user.getName()+"] location=["+user.getLocation()+"]");
}
}
}
The SQL is the same as the 1st SQL above.
SELECT this_.id AS id43_1_,
this_.name AS name43_1_,
user1_.id AS id44_0_,
user1_.EX_PROVIDER_ID AS EX4_44_0_,
user1_.location AS location44_0_,
user1_.name AS name44_0_
FROM EX_PROVIDER this_
INNER JOIN EX_USER user1_
ON this_.id=user1_.EX_PROVIDER_ID
WHERE user1_.location=?;
The log.
Criteria: provider=[0]
Criteria: user=[1] name=[User_2] location=[Berlin]
My question was and is, whether it is possible to get this result as proper entity tree with Provider_A containing exactly the User from Berlin in its list.
Thanks again,
Wolfgang B

JPA first level cache not clreared after completion of transaction

I am using JPA 2.1(with EclipseLink implementation), to get a record from Database.
By default it first level cache is enabled, it caches the record in PersistenceContext. If I try to get same record I will get it from first level cache, so no query will be fired on database second time.
Once transaction is over the first level cache will be cleared, and If I try to get same entry one more time, query has to be fired as the cache is cleared and it should come from database but it is not.
At least the query should be fired on database if I close the current entity manager,re-open it, and try to get the record.
Even now second query is not going to database. Once I get the record from the database first time(at this time I can see the select query in console logs), after wards if I try to get one more time, its coming from cache memory(as I can not see the query one more time in console logs, I am pretending that it is coming from cache), no matter what I do(use new transaction or close and re-open entity manager) the first level cache ain't cleared.
The code which I am using is below:
EntityManagerFactory entityManagerFactory=
Persistence.createEntityManagerFactory("01EmployeeBasics");
EntityManager entityManager=entityManagerFactory.createEntityManager();
System.out.println("EM1 : "+entityManager);
entityManager.getTransaction().begin();
System.out.println("Tx1 : "+entityManager.getTransaction());
Employee employee=entityManager.find(Employee.class, 123);
entityManager.getTransaction().commit();
entityManager.close();
entityManager=entityManagerFactory.createEntityManager();
System.out.println("EM2 : "+entityManager);
entityManager.getTransaction().begin();
System.out.println("Tx2 : "+entityManager.getTransaction());
Employee employee2=entityManager.find(Employee.class, 123);
entityManager.getTransaction().commit();
entityManager.close();
entityManagerFactory.close();
Employee class is as below:
package in.co.way2learn;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Employee {
#Id
private int id;
private String name;
private int salary;
public Employee() {
// TODO Auto-generated constructor stub
}
public Employee(int id, String name, int salary) {
super();
this.id = id;
this.name = name;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
System.out.println("Employee.getName()");
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
In database there is a record with id 123.
Now my question is why the first level cache is not cleared??
EclipseLink has a shared object (2nd level) cache which is enabled by default and which:
...exists for the duration of the persistence unit (EntityManagerFactory,
or server) and is shared by all EntityManagers and users of the
persistence unit.
If you disable this according to the instructions in the below then you should see the 2nd query firing.
https://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching

Creating an "IN" query with JPA 2.0 Criteria api

I am using tje JPA criteria API to create an "IN" query. I want to select Courses that are in certain Categories. The Categories are supposed to end up in the IN part of the query.
This is the Course entity. It has a reference to a Category entity, because each Course is in one Category.
#Entity
public class Course implements DomainObject {
private Long id;
private Integer version;
private String name;
private Category category;
#Override
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
}
In my service I want to select Courses that are belong to certain (a list) of Categories.
public List<Course> findCourses(CourseFilter filter) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Course> criteriaQuery = criteriaBuilder.createQuery(Course.class);
Root<Course> root = criteriaQuery.from(Course.class);
List<Predicate> predicateList = new ArrayList<Predicate>();
if (!filter.getCategories().isEmpty()) {
Predicate predicate = root.get(Course_.category).in(filter.getCategories());
predicateList.add(predicate);
}
Predicate[] predicates = new Predicate[predicateList.size()];
predicateList.toArray(predicates);
criteriaQuery.where(predicates);
TypedQuery<Course> typedQuery = entityManager.createQuery(criteriaQuery);
return typedQuery.getResultList();
}
When the query executes on the last line of the method it throws an error:
HTTP Status 500 - Request processing failed; nested exception is
org.springframework.dao.InvalidDataAccessApiUsageException:
org.hibernate.TransientObjectException: object references an unsaved transient instance
save the transient instance before flushing:nl.codebasesoftware.produx.domain.Category;
nested exception is java.lang.IllegalStateException:
org.hibernate.TransientObjectException: object references an unsaved transient instance
save the transient instance before flushing: nl.codebasesoftware.produx.domain.Category
I am not even sure I am using the right way to create an IN query. I think the criteria API is terribly complicated. But before I worry about the IN query I would like to know why Hibernate is throwing this TransientObjectException. The filter.getCategories() call results in actual categories, filled with a primary key id, etc.
Added:
Here is how I get the Category instance that I use to later fetch Courses with. This is also a DAO method that is called via a #Service from a #Controller method.
public Category findByName(String name) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Category> query = builder.createQuery(Category.class);
Root<Category> root = query.from(Category.class);
Predicate predicate = builder.equal(root.get(Category_.urlTitle), name);
query.where(predicate);
TypedQuery<Category> typedQuery = entityManager.createQuery(query);
return getSingleResult(typedQuery);
}
So, Hibernate is telling me I am using Category objects that somehow reference an unsaved entity, but I don't see how. The Category that is returned from this method is just a Category that if fetched by Hibernate. I am not doing anything with it before I send it to the method that fetches Courses.
Here is my the controller method:
#RequestMapping(method = RequestMethod.GET, value = "/{categoryUrlName}")
public String setup(#PathVariable("categoryUrlName") String categoryUrlName, Model model){
// Fetch the category
Category category = categoryService.findByName(categoryUrlName);
// if no category found, throw a 404
if(category == null){
throw new ResourceNotFoundException();
}
// Fetch courses in this category
List<Course> courses = courseService.findCourses(category);
model.addAttribute("courses", courses);
model.addAttribute("category", category);
model.addAttribute("mainContent", "content/category");
return "main";
}
Before executing a query, Hibernate flushes the changes you made to persistent entities in the session. This ensures that the query will search on the latest state of all the entities. Unfortunately, one of the dirty entities that Hibernate tries to flush references a transient entity, and thus can't be flushed, which causes the exception. The exception doesn't come from the query itself, but from the flush before the execution of the query.
You probably did something like the following before executing the query:
Cat cat = em.find(Cat.class, catId); // cat is a persistent persistent entity
cat.setMate(new Mouse()); // the mouse has not been persisted, and cat references it.

JPA persist Object with cascase relation and then delete it (hibernate)

I have the following bidirectional 1:n relationship between A user entity and a job entity:
User class:
...
#OneToMany(mappedBy = "user",cascade={CascadeType.PERSIST})
public Collection<Job> getJobs() {
return jobs;
}
public void addJob(Job j) {
jobs.add(j);
j.setUser(this);
}
...
Job class:
// Job class
...
#ManyToOne
public User getUser() {
return user;
}
Now saving a User object (and let hibernate automatically save the job entity) works...
Then when i want to delete the job entity from the db i get an exception ....
User user = new User();
user.addJob(new Job());
entityManager.getTransaction().begin();
entityManager.persist(user);
entityManager.getTransaction().commit();
Job j = entityManager.find(Job.class, 1L);
entityManager.getTransaction().begin();
entityManager.remove(j);
entityManager.getTransaction().commit();
Exception:
Exception in thread "main" javax.persistence.RollbackException: Error while committing the transaction
Caused by: javax.persistence.EntityNotFoundException: deleted entity passed to persist: [dst1.model.Job#<null>]
why does this happen? btw. when i explicitly call persist with the job object the problem does not occur... but i don't want to persist job seperately, but let hibernate persist them (with cascade, which actually works....)
thx
You need to remove the job from the user when you delete the entity:
entityManager.getTransaction().begin();
j.getUser().getJobs().remove(j);
entityManager.remove(j);
entityManager.getTransaction().commit();
Or by your own suggestion: Add orphanRemoval=true on the jobs collection.

JPA #GeneratedValue on id

In JPA, I am using #GeneratedValue:
#TableGenerator(name = "idGenerator", table = "generator", pkColumnName = "Indecator" , valueColumnName = "value", pkColumnValue = "man")
#Entity
#Table(name="Man")
public class Man implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "idGenerator")
#Column(name="ID")
private long id;
public void setId(Long i) {
this.id=i;
}
public Long getId(){
return id;
}
}
I initially set the ID to some arbitrary value (used as a test condition later on):
public class Sear {
public static void main(String[] args) throws Exception {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("testID");
EntityManager em = emf.createEntityManager();
Man man = new Man();
man.setId(-1L);
try {
em.getTransaction().begin();
em.persist(man);
em.getTransaction().commit();
} catch (Exception e) { }
if(man.getId() == -1);
}
}
}
What is the expected value of man.id after executing commit()? Should it be (-1), a newly generated value, or I should expect an exception?
I want to use that check to detect any exceptions while persisting.
What is the expected value of man.id after executing commit()? Should it be (-1), a newly generated value, or I should expect an exception?
You are just not supposed to set the id when using GeneratedValue. Behavior on persist will differ from one implementation to another and relying on this behavior is thus a bad idea (non portable).
I want to use that check to detect any exceptions while persisting.
JPA will throw a (subclass of) PersistenceException if a problem occurs. The right way to handle a problem would be to catch this exception (this is a RuntimeExeption by the way).
If you insist with a poor man check, don't assign the id and check if you still have the default value after persist (in your case, it would be 0L).
You setting the value of a field that is auto-generated is irrelevant. It will be (should be) set by the JPA implementation according to the strategy specified.
In EclipseLink this is configurable using the IdValidation enum and the #PrimaryKey annotation or the "eclipselink.id-validation" persistence unit property.
By default null and 0 will cause the id to be regenerated, but other values will be used. If you set the IdValidation to NEGATIVE, then negative numbers will also be replaced.
You can also configure your Sequence object to always replace the value.