All,
I am using JPA for this application and annotations for Mapping entities. I have an entity called UserStory and another one called Revision. There is a OneToMany for UserStory to Revision.
#Entity
#Table(name = "user_story")
#NamedNativeQueries({
#NamedNativeQuery(name = "storyBacklog", query = "SELECT userstory.rank AS rank, userstory.description AS description, userstory.estimate AS estimate, userstory.name AS name, "
+ "userstory.id AS id, userstory.status AS status FROM user_story userstory ORDER BY userstory.rank ASC", resultClass = UserStory.class),
#NamedNativeQuery(name = "getCos", query = "SELECT conditions.cos As cos FROM story_cos conditions WHERE conditions.story_id=?1", resultSetMapping = "cosMapping") })
#SqlResultSetMappings({ #SqlResultSetMapping(name = "cosMapping", columns = #ColumnResult(name = "cos")) })
public class UserStory implements Serializable {
private static final long serialVersionUID = 248298400283358441L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "story_revisions", joinColumns = #JoinColumn(name = "story_id"), inverseJoinColumns = #JoinColumn(name = "revision_id"))
private Set<Revision> revisions;
here's Revision entity:
#Entity
#Table(name = "revision")
public class Revision implements Serializable {
private static final long serialVersionUID = -1823230375873326645L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false)
private String description;
#Column(name = "date_created", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date creationDate;
When I create a userStory; I add a revision on to it,
but the join table is not populated unless, I persist story first,
then add revision and merge it.
here's the code for saving a UserStory:
public UserStory saveUserStory(UserStory userStory) {
Revision revision = new Revision();
revision.setCreationDate(new Timestamp(System.currentTimeMillis()));
revision.setDescription("User story created");
Set<Revision> revisions = new HashSet<Revision>();
revisions.add(revision);
userStory.setRevisions(revisions);
return storyDao.create(userStory);
}
in StoryDao I call the persist method:
#Transactional(readOnly = false)
public UserStory create(UserStory userStory) {
if (userStory.getRank() == null) {
Integer highestRank = 0;
highestRank = (Integer) entityManager.createNativeQuery("select max(rank) from user_story")
.getSingleResult();
if (highestRank != null)
highestRank += 1;
else
highestRank = new Integer(1);
userStory.setRank(highestRank);
}
entityManager.persist(userStory);
LOGGER.debug("Added User Story with id " + userStory.getId());
entityManager.detach(userStory);
return userStory;
}
here's the SQL from LOGS
Hibernate:
insert
into
user_story
(description, estimate, name, rank, status)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
revision
(date_created, description)
values
(?, ?)
Hibernate:
select
revision0_.id as id5_0_,
revision0_.date_created as date2_5_0_,
revision0_.description as descript3_5_0_
from
revision revision0_
where
revision0_.id=?
Hibernate:
select
userstory0_.id as id3_1_,
userstory0_.description as descript2_3_1_,
userstory0_.estimate as estimate3_1_,
userstory0_.name as name3_1_,
userstory0_.rank as rank3_1_,
userstory0_.status as status3_1_,
revisions1_.story_id as story1_3_3_,
revision2_.id as revision2_3_,
revision2_.id as id5_0_,
revision2_.date_created as date2_5_0_,
revision2_.description as descript3_5_0_
from
user_story userstory0_
left outer join
story_revisions revisions1_
on userstory0_.id=revisions1_.story_id
left outer join
revision revision2_
on revisions1_.revision_id=revision2_.id
where
userstory0_.id=?
I can see from here it saves the user story and revision, but then tries to run a join to see if the relation exists before doing an insert into the join table. Which of course it will not find because I am creating this object.
How do it get the join table populated in this case?
Works now. Here's the updated code
revisions.add(revision);
userStory = storyDao.create(userStory);
userStory.setRevisions(revisions);
return storyDao.update(userStory);
I am still not sure why this is required; the two step method where I persist an object then update it.
Related
Hi i have these 2 basic entity mapping for postgresql db, and i have wrote criteria for
fetching all activated user which have same key it is showing this error
org.hibernate.QueryException: could not resolve property: key.id of: com.sar.dfsapp.modal.ActivatedUser
#Entity
#Table(name = "activated_user")
public class ActivatedUser implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, length = 11)
private long id;
#ManyToOne
#JoinColumn(name = "key_id", nullable = false)
private Key key;
}
#Entity
#Table(name = "key")
public class Key implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, length = 11)
private long id;
#Column(name = "key_code", nullable = false)
private String keyCode;
}
Below is my criteria i have tried.
Criteria c = getSession().createCriteria(ActivatedUser.class);
c.add(Restrictions.eq("key.id", id));
List<ActivatedUser> result = c.list();
try this :
Criteria c = getSession().createCriteria(ActivatedUser.class);
Criteria keyCriteria = criteria.createCriteria("key", CriteriaSpecification.INNER_JOIN);
keyCriteria.add(Restrictions.eq("id", id));
List<ActivatedUser> result = c.list();
it there the same error ?
I have two table:
CREATE TABLE [LeTYPE](
[LeNAME] [varchar](100) NOT NULL,
[Le_DESC] [varchar](500) NULL,
[LeFOR] [varchar](50) NOT NULL,
CONSTRAINT [PK_LeTYPE] PRIMARY KEY CLUSTERED
(
[LeNAME] ASC
)
)
CREATE TABLE [Le](
[SN] [int] IDENTITY(1,1) NOT NULL,
[LeNAME_FK] [varchar](100) NOT NULL,
[Le_SN] [int] NULL,
[LOWERRANGE] [float] NOT NULL,
[UPPERRANGE] [float] NOT NULL,
[Le_DESC] [varchar](500) NULL,
[COLOR] [varchar](45) NULL,
CONSTRAINT [Le_pk] PRIMARY KEY CLUSTERED
(
[SN] ASC
))
GO
ALTER TABLE [Le] WITH CHECK ADD CONSTRAINT [FK_Le_LeTYPE] FOREIGN KEY([LeNAME_FK])
REFERENCES [LeTYPE] ([LeNAME])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [Le] CHECK CONSTRAINT [FK_Le_LeTYPE]
GO
One tuple in LETYPE will have many LE.
JPA Entity generated by netbeans:
public class Letype implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 100)
#Column(nullable = false, length = 100)
private String Lename;
#Size(max = 500)
#Column(name = "Le_DESC", length = 500)
private String LeDesc;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 50)
#Column(nullable = false, length = 50)
private String Lefor;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "LenameFk", fetch = FetchType.LAZY)
private List<Le> LeList;
}
public class Le implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#NotNull
#Column(nullable = false)
private Integer sn;
#Column(name = "Le_SN")
private Integer LeSn;
#Basic(optional = false)
#NotNull
#Column(nullable = false)
private double lowerrange;
#Basic(optional = false)
#NotNull
#Column(nullable = false)
private double upperrange;
#Size(max = 500)
#Column(name = "Le_DESC", length = 500)
private String LeDesc;
#Size(max = 45)
#Column(length = 45)
private String color;
#JoinColumn(name = "LeNAME_FK", referencedColumnName = "LeNAME", nullable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Letype LenameFk;
}
Now, What I wanted was if I add a LETYPE from JSF view I would like to add multiple LE also at the same time.
LETYPE
-LE1
-LE2
-LE3
Do I need to set LenameFk manually in Le entity since I am getting
*Cannot insert the value NULL into column 'LENAME_FK'*? Why won't it automatically take it from Le enityt?
Note this snippet of code:
public class Le implements Serializable {
...
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Letype LenameFk;
...
}
optional = false means any instance of this entity must participate the relation, so, the foreign key field can not be null.
Your right, You need to set LenameFk manually in Le entity.
In General , for Bi-directional one-to-many two way relation , Accessor method should like below and assume entities are Customer and Order and one-to-many relation b/w them.
Customer.java
public Collection<Order> getOrders() {
return Collections.unmodifiableCollection(orders);
}
public void addToOrders(Order value) {
if (!orders.contains(value)) {
orders.add(value);
value.setCustomer(this);
}
}
public void removeFromOrders(Order value) {
if (orders.remove(value)) {
value.setCustomer(null);
}
}
Order.java
public void setCustomer(Customer value) {
if (this.customer != value) {
if (this.customer != null) {
this.customer.removeFromOrders(this);
}
this.customer = value;
if (value != null) {
value.addToOrders(this);
}
}
}
public Customer getCustomer() {
return customer;
}
I'm trying to learn and understand JPA, and just have a couple of questions regarding deleting a parent and its children in one go. I'm using OpenJPA and EJB3. I have two entities, a Category and a Product. A Category contains many products and a product has a reference to its parent category. The category's list of products is set to cascade.
//Category
#Entity #NamedQueries({#NamedQuery(name = "Category.getCategoryByName", query = "SELECT c FROM Category c WHERE c.name = :name"),#NamedQuery(name = "Category.getCategoryByCategoryId", query = "SELECT c FROM Category c WHERE c.categoryid = :categoryid"), #NamedQuery(name = "Category.getAllCategories", query = "SELECT c FROM Category c left join fetch c.products")})
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=IDENTITY)
private Integer categoryid;
private String name;
//bi-directional many-to-one association to Product
#OneToMany(cascade={CascadeType.ALL}, orphanRemoval = true,
fetch = EAGER, mappedBy="category")
private List<Product> products;
}
//Product
#Entity
#NamedQueries({#NamedQuery(name = "Product.getProductsByCategory",
query = "SELECT p.code, p.description, p.name, p.productid, p.weight FROM Product p WHERE p.category.categoryid = :category_categoryid"),
#NamedQuery(name = "Product.getProductByName", query = "SELECT p FROM Product p WHERE p.name = :name"),
#NamedQuery(name = "Product.getProductByCode", query = "SELECT p FROM Product p WHERE p.code = :code"),
#NamedQuery(name = "Product.getProductByProductId", query = "SELECT p FROM Product p WHERE p.productid = :productid"),
#NamedQuery(name = "Product.getAllProducts", query = "SELECT p FROM Product p")})
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=IDENTITY)
private Integer productid;
private String code;
private String description;
private String name;
private Double weight;
//bi-directional many-to-one association to Category
#ManyToOne(optional = false)
#JoinColumn(name="CATEGORYID")
private Category category;
}
}
// The EJB
#Stateless
#LocalBean
public class ShopManagerBean implements Serializable {
#PersistenceContext(unitName = "TestEJBProject2", type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteCategory(Category category)
throws TestApplicationException {
try {
Category actualCat = entityManager.find(Category.class,
category.getCategoryid());
List<Product> products = actualCat.getProducts();
if (products != null) {
Iterator<Product> it = products.iterator();
while (it.hasNext()) {
Product p = it.next();
it.remove();
entityManager.remove(p);
}
}
entityManager.refresh(actualCat);
entityManager.remove(actualCat);
} catch (Exception e) {
e.printStackTrace();
throw new TestApplicationException("Error creating new Product", e);
}
}
}
If I use the following code in the deleteCategory method the EJB then I cannot delete the parent and children as I get an Optimistic Locking exception (An optimistic lock violation was detected when flushing object instance "entity.Product-101" to the data store. This indicates that the object was concurrently modified in another transaction.) - complaining about flushing the product child to the data store
Category actualCat = entityManager.find(Category.class, category.getCategoryid());
if (products != null) {
actualCat.getProducts().clear();
}
entityManager.remove(actualCat);
However, if I use the following code in the deleteCategory method then I can delete the parent and children...but only if I call entityManager.refresh(actualCat) after removing the children and before removing the parent (otherwise I get an optimistic locking exception). Could somebody please explain to me why this is the case and also what the correct/best way of doing a cascading delete with OpenJPA V2 would be?
Category actualCat = entityManager.find(Category.class, category.getCategoryid());
List<Product> products = actualCat.getProducts();
if (products != null) {
Iterator<Product> it = products.iterator();
while (it.hasNext()) {
Product p = it.next();
it.remove();
entityManager.remove(p);
}
}
entityManager.refresh(actualCat);
entityManager.remove(actualCat);
Thanks in advance for your help
Fais
Addition
Here is the db creation script:
--
CREATE SCHEMA "DB2ADMIN";
CREATE TABLE "DB2ADMIN"."CATEGORY" (
"CATEGORYID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 20),
"NAME" VARCHAR(50) NOT NULL
)
DATA CAPTURE NONE;
CREATE TABLE "DB2ADMIN"."PRODUCT" (
"PRODUCTID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 20),
"CODE" CHAR(15) NOT NULL,
"NAME" VARCHAR(50) NOT NULL,
"DESCRIPTION" VARCHAR(200) NOT NULL,
"WEIGHT" FLOAT(53) NOT NULL,
"CATEGORYID" INTEGER NOT NULL
)
DATA CAPTURE NONE;
ALTER TABLE "DB2ADMIN"."CATEGORY" ADD CONSTRAINT "CATEGORY_PK" PRIMARY KEY
("CATEGORYID");
ALTER TABLE "DB2ADMIN"."PRODUCT" ADD CONSTRAINT "PRODUCT_PK" PRIMARY KEY
("PRODUCTID");
ALTER TABLE "DB2ADMIN"."PRODUCT" ADD CONSTRAINT "PRODUCT_CATEGORY_FK" FOREIGN KEY
("CATEGORYID")
REFERENCES "DB2ADMIN"."CATEGORY"
("CATEGORYID")
ON DELETE CASCADE;
I'm trying to implement loading in three tables (the beginning of the problem with mapping)
Products:
#Entity
#Table(name = "products")
public class Product implements Serializable {
#Id
#Column(name = "id")
private Integer id;
#OneToMany(mappedBy = "property", fetch = FetchType.LAZY)
private Collection<ProductProperty> productPropertyCollection;
...
}
Properties:
#Entity
#Table(name = "properties")
public class Property implements Serializable {
#Id
#Column(name = "id")
private Integer id;
#OneToMany(mappedBy = "property", fetch = FetchType.LAZY)
private Collection<ProductProperty> productPropertyCollection;
...
}
Product_Property
#Entity
#Table(name = "product_property")
public class ProductProperty implements Serializable {
#EmbeddedId
protected ProductPropertyPK productPropertyPK;
#MapsId(value = "propertyId")
#JoinColumn(name = "property_id", referencedColumnName = "id")
#ManyToOne()
private Property property;
#MapsId(value = "productId")
#JoinColumn(name = "product_id", referencedColumnName = "id")
#ManyToOne()
private Product product;
...
}
#Embeddable
public class ProductPropertyPK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "product_id", insertable = false, updatable = false)
private int productId;
#Basic(optional = false)
#NotNull
#Column(name = "property_id", insertable = false, updatable = false)
private int propertyId;
...
}
It works fine for 1, 10, 100 products, but somewhere there is an error, because for 1000 and more products throws error:
Caused by: java.lang.StackOverflowError
at java.util.HashMap.getEntry(HashMap.java:443)
at java.util.HashMap.containsKey(HashMap.java:434)
at java.util.HashSet.contains(HashSet.java:201)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.discoverAndPersistUnregisteredNewObjects(UnitOfWorkImpl.java:4141)
at org.eclipse.persistence.mappings.ObjectReferenceMapping.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectReferenceMapping.java:938)
at org.eclipse.persistence.mappings.ObjectReferenceMapping.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectReferenceMapping.java:916)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectBuilder.java:1964)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.discoverAndPersistUnregisteredNewObjects(UnitOfWorkImpl.java:4178)
at org.eclipse.persistence.mappings.CollectionMapping.cascadeDiscoverAndPersistUnregisteredNewObjects(CollectionMapping.java:426)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectBuilder.java:1964)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.discoverAndPersistUnregisteredNewObjects(UnitOfWorkImpl.java:4178)
at org.eclipse.persistence.mappings.ObjectReferenceMapping.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectReferenceMapping.java:938)
at org.eclipse.persistence.mappings.ObjectReferenceMapping.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectReferenceMapping.java:916)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectBuilder.java:1964)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.discoverAndPersistUnregisteredNewObjects(UnitOfWorkImpl.java:4178)
at org.eclipse.persistence.mappings.CollectionMapping.cascadeDiscoverAndPersistUnregisteredNewObjects(CollectionMapping.java:426)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectBuilder.java:1964)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.discoverAndPersistUnregisteredNewObjects(UnitOfWorkImpl.java:4178)
at org.eclipse.persistence.mappings.ObjectReferenceMapping.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectReferenceMapping.java:938)
at org.eclipse.persistence.mappings.ObjectReferenceMapping.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectReferenceMapping.java:916)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.cascadeDiscoverAndPersistUnregisteredNewObjects(ObjectBuilder.java:1964)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.discoverAndPersistUnregisteredNewObjects(UnitOfWorkImpl.java:4178)
...
when i'm creating ProductProperty, i'm setting product and property in ProductProperty, and adding to collection for bidirection in Product and Property.
where i could make a mistake?
Looks like your object model complexity or depth is just difficult to traverse within your JVM's stack limits. As it is, every entity seems reachable from every other entity, which causes problems when traversed recursively. Try increasing the -Xss setting. You might also reduce the interconnectivity, such as removing one of the OneToMany mappings and query for it directly instead of storing it in the Product or Property mapping. You might also file an enhancement with EclipseLink to traverse the object graph using a stack instead of recursively.
I am trying my way around JPA but I cant get this to work the way I understand them.
Its the onetoone bidirectional mapping between an Order and an OrderInvoice class which is a required
association
My entities are marked as this
#Entity
#Table(name = "Orders")
public class Order {
#Id
#GeneratedValue
#Column(name = "ORDER_ID")
private int orderId;
#OneToOne(optional=false,cascade=CascadeType.ALL, mappedBy="order", targetEntity=OrderInvoice.class)
private OrderInvoice invoice;
}
#Entity
#Table(name = "ORDER_INVOICE")
public class OrderInvoice {
#Id
#GeneratedValue
#Column(name = "INVOICE_ID", nullable = false)
private int invoiceId;
#OneToOne(optional = false)
#JoinColumn(name="ORDER_ID")
private Order order;
}
My test class is like this.
#Test
public void createOrder() {
Order order = createOrderImpl();
assertNotNull(order);
}
private Order createOrderImpl() {
OrderInvoice orderInvoice = new OrderInvoice(new Date(), 100.0, null,
null, new Date());
Order order = new Order(100.0, "JOHN Doe's Order", new Date(), new Date(),orderInvoice);
orderDao.create(order);
return order;
}
But I am encountering below problem when I run my Test
javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: order.OrderInvoice.order
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: order.OrderInvoice.order
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:95)
try to
orderInvoice.setOrder(order);
orderDao.create(order);