I am actively learning JPA and EJBs and I came to the following situation: I have one EJB (named SecondController) which implements a method for persisting an entity. Let's call it SecondEntity. There is a requirement that when the entity is persisted its field dateInserted is set to the current date (in practice the requirements may be more complex - I want to know if the concept is OK or if I am going totally in the wrong direction).
Now I define a new entity - let's call it FirstEntity - which contains SecondEntity as an attribute. I implement another EJB which implements a method for persisting FirstEntity - so each time FirstEntity is persisted, SecondEntity must be persisted too. I know I can use #ManyToOne(optional = false, cascade = CascadeType.PERSIST) on the secondEntity attribute in FirstEntity (that would do auto persist). But remember, there is a dateInserted requirement and logic is already implemented. The idea is to reuse the already implemented method for persisting SecondEntity in SecondController by injecting it into FirstController.
#Entity
public class SecondEntity implements Serializable
{
}
#Entity
public class FirstEntity implements Serializable
{
#JoinColumn(name = "second_id", referencedColumnName = "second_id")
#ManyToOne(optional = false)
private SecondEntity secondEntity;
}
#Stateless
public class SecondController implements Serializable
{
#PersistenceUnit(name = "PU")
private EntityManagerFactory emf;
public SecondController()
{
}
public void create(SecondEntity secondEntity)
{
EntityManager em = emf.createEntityManager();
try
{
secondEntity.setDateInserted(new Date(System.currentTimeMillis()));
em.persist(secondEntity);
}
finally
{
em.close();
}
}
}
#Stateless
public class FirstController implements Serializable
{
#PersistenceUnit(name = "PU")
private EntityManagerFactory emf;
#EJB
private SecondController secondController;
public FirstController()
{
}
public void create(FirstEntity firstEntity)
{
EntityManager em = emf.createEntityManager();
try
{
secondController.create(firstEntity.getSecondEntity());
em.persist(firstEntity);
}
finally
{
em.close();
}
}
}
The point of the sample code above is to call secondController.create(firstEntity.getSecondEntity()). Is this OK or is it considered a bad practice? I have two such cases in my application and one works (SecondEntity is optional - can be null), and one which does not (SecondEntity is mandatory - cannot be null). The code fails at em.persist(firstEntity) by saying:
java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST
If I replace the mentioned call with em.persist(firstEntity.getSecondEntity) the code works fine.
How can I solve this problem?
You shouldn't set the dateInserted field in your EJB. Do it in a PrePersist event hook. This way the persistence provider will make sure your hook is always called automatically before persisting an entity. Then you can let persist to be cascaded from parent entity to the other (#ManyToOne(optional = false, cascade = CascadeType.PERSIST)).
#Entity
public class SecondEntity implements Serializable {
#PrePersist
public void prePersist() {
if (dateInserted == null) {
dateInserted = new Date();
}
}
}
Related
I have mapped three JPA classes, just like this.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "super_table")
public abstract class SuperClass {
#Id
public Integer getId() {
return id;
}
...
}
#Entity
#Table(name = "child_table_a")
#PrimaryKeyJoinColumn(name = "fk_id_super_table")
public class ChildA extends SuperClass {
...
#Column(name = "serial_code", nullable = true, unique = true)
public String getSerialCode() {
return serialCode;
}
...
}
#Entity
#Table(name = "child_table_b")
#PrimaryKeyJoinColumn(name = "fk_id_super_table")
public class ChildB extends SuperClass {
...
}
So, there is a method to persist objects. This method is annotated by #Transactional annotation. My obvious intention is avoid persist something wrong or incompletely.
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
public void salvar(SuperClass obj) {
getEntityManager().persist(obj);
}
When the object is ok, the method performs right. When the method try to persist an object ChildA, if the fields of superclass are ok, but the field serialCode has a value existent on table (unique constraint), the annotated method doesn't throws any exception. When Spring framework tries to commit, it throws an exception (about the data base unique constraint). However the tuple of table super_table is inserted and committed.
Can somebody help me to solve this problem?
I'm using:
- Hibernate 4.0.1.Final
- Spring 3.2.2
- JBoss AS 7.1
I want to choose when to load Set with Lazy
This is my entity Product
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Type(type = "objectid")
private String id;
private String name;
#ManyToMany(fetch = FetchType.LAZY)
private Set<Category> categories;
This is my AbstractService
public abstract class AbstractService<T> {
private Class<T> entityClass;
public AbstractService(Class<T> entityClass){
this.entityClass=entityClass;
public List<T> findAll(){
Query nativeQuery = getEntityManager().createNativeQuery("{}", entityClass);
return getEntityManager().createNativeQuery("{}",entityClass).getResultList();
}
}
this i my ProductService
#Stateless
public class ProductService extends AbstractService<Product> {
#PersistenceContext(unitName = "name-db")
private EntityManager em;
public ProductService() {
super(Product.class);
}
#Override
protected EntityManager getEntityManager() {
return em;
}
this is my test method
#Test
public void testFindAllProducts()throws Exception {
List<Product> products = productService.findAll();
for (Product p:
products ) {
//Hibernate.initialize(p.getCategories());
System.out.println(p.toString());
}
}
I would like to use a proxy to initialize the categories every time I need it
Hibernate (including Hibernate OGM) supports lazy loading using proxy objects instead of
instances of the entity classes.
Hibernate uses proxies as a solution for “breaking up” the interconnected data received from a database into
smaller pieces that can be easily stored in memory. It may be useful to be aware that Hibernate dynamically generates
proxies for objects that are lazily loaded. Chances are, you aren’t aware of proxy objects, and won’t be until you get
some exceptions of type LazyInitializationException, or until you try to test lazy loading in a debugger and notice
the presence of some not-null objects with null properties. Not knowing when you’re “working” on a proxy object
instead of an entity object can cause weird results or exceptions
How can i implement a proxy?
You don't have to implement a proxy, Hibernate OGM will instantiate one as needed.
In this case, for each result the value returned by p.getCategories() should be a proxy.
Using Java and Spring, I have an entity that is modified but has not yet been persisted. I want to compare it to its original state, the state that still exists in the database. However, when I do a fetch for the entity to get the old state, it always returns the entity in the modified state.
An example, to better illustrate the point, is shown below.
The MyEntity class represents the entity.
The MyEntityRepository class is a Spring Data Repository used for performing database interactions for the MyEntity entity.
The MyService class has a transactional method called save which compares the new version of MyEntity (called entity) with oldEntity. If entity and oldEntity are different, then entity is saved to the database; otherwise, it is not saved.
The oldEntity is fetched from the repository and compared against entity. However, when this is done, oldEntity is always the same as entity. (The changed values in entity are reflected in oldEntity.)
Since entity is not yet saved, how can I fetch the state of the entity as it currently exists in the database and not in the current context? Is it possible using Spring annotations or setting a propagation value to go outside of the current persistence context to fetch the entity so that it is the real, persisted value and not the one in the current context?
#Service
public class MyService {
#Autowire
private MyEntityRepository repository;
#Transactional
public boolean save(MyEntity entity) {
MyEntity oldEntity = repository.findOne(entity.getId());
if (entity.compareTo(oldEntity) != 0) {
repository.save(entity);
}
}
}
public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, Long> {
MyEntity findByName(String name);
}
#Entity
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#Column(nullable = false)
protected String name;
public Long getId() { ... }
public void setId(Long id) { ... }
public String getName() { ... }
public void setName(String name) { ... }
public int compareTo(MyEntity entity) { ... }
}
You need to keep in mind the behaviour of how transactions work with entities. When you do MyEntity entity = myEntityRepository.findByName("test"); And call something like entity.setName(); within a transaction, then once the transaction is finished the name column in the database will be updated. You will need to use the entity manager to remove an entity within a transaction before calling setName to stop the database from being updated. But then you will need to re-add it if you wanted to save the changes to name.
I am trying to setup some foreign key links to a static lookup table using JPA 2.0. the entity that contains the link and the static lookup table is defined like this in class A and Status respectively. I also have an entity called AHistory that is a clone of A in all regards. When I call the logNewA method, I find the already existing Entity Status from the em, create a new A entity and then I create a corresponding AHistory entity as seen below. I am getting this error when i try to do this. can any one help me out?
#Entity
#Table(name = "ATABLE")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class A extends BaseBusinessObject {
#Id
#Column(name = "AID"
private long id;
#JoinColumn(name = "STATUSID")
#ManyToOne
private Status status;
// other irrelevant fields in BaseBusinessObject class.
}
public class AHistory extends BaseBusinessObject {
#Id
#Column(name = "AHISTORYID"
private long id;
#JoinColumn(name = "STATUSID")
#ManyToOne
private Status status;
public AHistory(Status status){
this.status = status;
}
// other irrelevant fields in BaseBusinessObject class.
}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "STATUSTABLE")
public class Status extends BaseBusinessType {
#Id
#Column(name = "STATUSID")
private long id;
// other irrelevant fields in BaseBusinessType class.
}
#Stateless
#LocalBean
public class SomeEJB{
#PersistenceContext
EntityManager em;
#EJB
ProcessorBean processor;
...
public A logNewA()
throws SystemException {
Status status = em.find(/*...NEW-STATUS*/); //assume this is now attached
A a = new A(status);
return processor.saveA(a);
}
}
#Stateless
#LocalBean
public class ProcessorBean {
#PersistenceContext
EntityManager em;
...
public A saveA(A a) throws SystemException {
AHistory history = new AHistory(a.getStatus());
process.getAHistorys().add(history);
return em.merge(a);
}
}
Caused by: <openjpa-2.1.2-SNAPSHOT-r422266:1497841 nonfatal user error> org.apache.openjpa.persistence.InvalidStateException: Encountered unmanaged object "au.com.combined.domain.Status-1" in life cycle state unmanaged while cascading persistence via field "au.com.combined.domain.AHistory.status" during flush. However, this field does not allow cascade persist. You cannot flush unmanaged objects or graphs that have persistent associations to unmanaged objects.
Suggested actions: a) Set the cascade attribute for this field to CascadeType.PERSIST or CascadeType.ALL (JPA annotations) or "persist" or "all" (JPA orm.xml),
b) enable cascade-persist globally,
c) manually persist the related field value prior to flushing.
d) if the reference belongs to another context, allow reference to it by setting StoreContext.setAllowReferenceToSiblingContext().
FailedObject: au.com.combined.domain.Status-1
at org.apache.openjpa.kernel.SingleFieldManager.preFlushPC(SingleFieldManager.java:775)
at org.apache.openjpa.kernel.SingleFieldManager.preFlush(SingleFieldManager.java:610)
at org.apache.openjpa.kernel.SingleFieldManager.preFlush(SingleFieldManager.java:578)
at org.apache.openjpa.kernel.SingleFieldManager.preFlush(SingleFieldManager.java:494)
at org.apache.openjpa.kernel.StateManagerImpl.preFlush(StateManagerImpl.java:2971)
at org.apache.openjpa.kernel.PNewProvisionalState.nonprovisional(PNewProvisionalState.java:45)
at org.apache.openjpa.kernel.StateManagerImpl.nonprovisional(StateManagerImpl.java:1222)
at org.apache.openjpa.kernel.SingleFieldManager.preFlushPC(SingleFieldManager.java:812)
at org.apache.openjpa.kernel.SingleFieldManager.preFlushPCs(SingleFieldManager.java:751)
at org.apache.openjpa.kernel.SingleFieldManager.preFlush(SingleFieldManager.java:653)
at org.apache.openjpa.kernel.SingleFieldManager.preFlush(SingleFieldManager.java:578)
at org.apache.openjpa.kernel.SingleFieldManager.preFlush(SingleFieldManager.java:494)
at org.apache.openjpa.kernel.StateManagerImpl.preFlush(StateManagerImpl.java:2971)
at org.apache.openjpa.kernel.PNewState.beforeFlush(PNewState.java:40)
at org.apache.openjpa.kernel.StateManagerImpl.beforeFlush(StateManagerImpl.java:1047)
at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2096)
at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2056)
at org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java:1974)
at com.ibm.ws.uow.ComponentContextSynchronizationWrapper.beforeCompletion(ComponentContextSynchronizationWrapper.java:65)
at com.ibm.tx.jta.impl.RegisteredSyncs.coreDistributeBefore(RegisteredSyncs.java:291)
at com.ibm.ws.tx.jta.RegisteredSyncs.distributeBefore(RegisteredSyncs.java:152)
at com.ibm.ws.tx.jta.TransactionImpl.prePrepare(TransactionImpl.java:2367)
at com.ibm.ws.tx.jta.TransactionImpl.stage1CommitProcessing(TransactionImpl.java:575)
at com.ibm.tx.jta.impl.TransactionImpl.processCommit(TransactionImpl.java:1015)
... 85 more
Solved
The problem was that inside the saveA function (which i paraphrased for secrecy's sake), i was calling a Bean that had a reference to a different persistence context. after explicitly stating the unitName in the #PersistenceContext(unitName = "Domain") EntityManager em for the bean, the problem dissappeared.
Because the other entity manager was finding the Status object, the different entitymanager (in charge of actually calling em.merge()) did not recognise the object as managed, therefore it attempted to persist it, thus causing the error.
I'm using JPA (EclipseLink) and Spring. Say I have a simple entity with an auto-generated ID:
#Entity
public class ABC implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
// ...
}
In my DAO class, I have an insert method that calls persist() on this entity. I want the method to return the generated ID for the new entity, but when I test it, it returns 0 instead.
public class ABCDao {
#PersistenceContext
EntityManager em;
#Transactional(readOnly=false)
public int insertABC(ABC abc) {
em.persist(abc);
// I WANT TO RETURN THE AUTO-GENERATED ID OF abc
// HOW CAN I DO IT?
return abc.id; // ???
}
}
I also have a service class that wraps the DAO, if that makes a difference:
public class ABCService {
#Resource(name="ABCDao")
ABCDao abcDao;
public int addNewABC(ABC abc) {
return abcDao.insertABC(abc);
}
}
The ID is only guaranteed to be generated at flush time. Persisting an entity only makes it "attached" to the persistence context. So, either flush the entity manager explicitely:
em.persist(abc);
em.flush();
return abc.getId();
or return the entity itself rather than its ID. When the transaction ends, the flush will happen, and users of the entity outside of the transaction will thus see the generated ID in the entity.
#Override
public ABC addNewABC(ABC abc) {
abcDao.insertABC(abc);
return abc;
}
#Entity
public class ABC implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
}
check that #GeneratedValue notation is there in your entity class.This tells JPA about your entity property auto-generated behavior
This is how I did it:
EntityManager entityManager = getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(object);
transaction.commit();
long id = object.getId();
entityManager.close();
You could also use GenerationType.TABLE instead of IDENTITY which is only available after the insert.
Another option compatible to 4.0:
Before committing the changes, you can recover the new CayenneDataObject object(s) from the collection associated to the context, like this:
CayenneDataObject dataObjectsCollection = (CayenneDataObject)cayenneContext.newObjects();
then access the ObjectId for each one in the collection, like:
ObjectId objectId = dataObject.getObjectId();
Finally you can iterate under the values, where usually the generated-id is going to be the first one of the values (for a single column key) in the Map returned by getIdSnapshot(), it contains also the column name(s) associated to the PK as key(s):
objectId.getIdSnapshot().values()
This is how I did it. You can try
public class ABCService {
#Resource(name="ABCDao")
ABCDao abcDao;
public int addNewABC(ABC abc) {
ABC.setId(0);
return abcDao.insertABC(abc);
}
}
em.persist(abc);
em.refresh(abc);
return abc;