JPA related entities persist - java

I've designed some tables for my application where I've placed something like that:
product productversion
--- ---
productId productVersionId
productVersionId (ref) productId (ref)
name length
height
where we have relation one to many. I just need some base product with parameters which can change and versioned parameters so I can watch it's history. Now... I have problem with Persisting those objects. I've tried multiple ways, this is oldest one:
Product p = new Product();
p.name = "some name";
Productversion pv = new Productversion();
pv.length = 10;
pv.height = 20;
p.productversionId = pv;
But while trying to persist this object I've getting error that productId cannot be null and it refers to field productId inside Productversion object. How can I do this?
[update]
Some more information about my problem:
public class Product implements Serializable {
#JoinColumn(name = "productversionId", referencedColumnName = "productversionId")
#ManyToOne(optional = true, cascade = CascadeType.PERSIST)
private Productversion productversionId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "productId")
private Collection<Productversion> productversionCollection;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ProductId")
private Integer productId;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 255)
#Column(name = "Name")
private String name = null;
// well I believe construct and getter/setter not important now
And second Entity:
public class Productversion implements Serializable {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "productversionId")
private Collection<Productiondata> productiondataCollection;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "productversionId")
private Integer productversionId;
#Basic(optional = false)
#NotNull
#Column(name = "parameter")
private float weight;
And the way I create new object (just have no idea how can I do this):
public Product getNewProduct() {
if (newProduct == null) {
this.newProduct = new Product();
Productversion pv = new Productversion();
pv.setProductId(newProduct);
newProduct.setProductversionId(pv);
}
return newProduct;
}
And the way I'm trying to persist object:
public Boolean addEntry(Product productData) {
Boolean status = false;
DbConnector dc = new DbConnector();
EntityManager em = dc.getEntityManager();
try {
EntityTransaction et = em.getTransaction();
et.begin();
em.persist(productData);
et.commit();
status = true;
} finally {
em.close();
}
return status;
}

Seems like you missing on the set for the id field in your Product object.
Product p = new Product();
p.id = 1; <<<<<<-------------------
p.name = "some name";

Related

Populate DTO using query with JOIN

I have this main Product table:
#Table(name = "product")
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(name = "user_id", length = 20)
private Integer userId;
#Column(name = "title", length = 75)
private String title;
#Column(name = "meta_title", length = 100)
private String metaTitle;
#Column(name = "status", length = 100)
private String status;
}
Additional table for storing categories that should be returned as List:
#Table(name = "product_category")
public class ProductCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(name = "product_id", length = 4)
private Integer productId;
#Column(name = "category_id", length = 20)
private Integer categoryId;
}
Additional table for storing Payment Methods that should be returned as List:
#Table(name = "product_payment_methods")
public class ProductPaymentMethods implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(name = "product_id", length = 20)
private Integer productId;
#Column(name = "payment_methods", length = 20000)
private String paymentMethods;
}
I want to return a result like this:
id | title | categoryId | paymentMethods |
1 | test | 34, 43 | 345, 7, 5 |
5 | test2 | 64,5, 3 | 654, 3, 5 |
I tried this:
SELECT *
FROM Product
INNER JOIN product_category ON Product.id = product_category.productId
INNER JOIN product_payment_methods ON Product.id = product_payment_methods.productId
WHERE userId = 1
What is the proper way to populate this DTO?
public class ProductFullDTO {
private int id;
private Integer userId;
private List<Integer> categories;
private List<String> paymentMethods;
}
If, as indicated in your comments, you need query your information with HQL a good way to proceed can be the following.
First, modify your Product entity an include relationships for both ProductCategory and ProductPaymentMethods, something like:
#Table(name = "product")
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(name = "user_id", length = 20)
private Integer userId;
#Column(name = "title", length = 75)
private String title;
#Column(name = "meta_title", length = 100)
private String metaTitle;
#Column(name = "status", length = 100)
private String status;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProductCategory> categories;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List< ProductPaymentMethods> paymentMethods;
// Setters and getters, omitted for brevity
}
Modify both ProductCategory and ProductPaymentMethods to accommodate the entities relationship:
#Table(name = "product_category")
public class ProductCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
// Please, feel free to change the insertable and updatable attributes to
// fit your needs
#Column(name = "product_id", length = 4, insertable=false, updatable=false)
private Integer productId;
#ManyToOne(fetch= FetchType.LAZY)
#JoinColumn(name="product_id")
private Product product;
#Column(name = "category_id", length = 20)
private Integer categoryId;
// Setters and getters, omitted for brevity
}
#Table(name = "product_payment_methods")
public class ProductPaymentMethods implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
// Please, feel free to change the insertable and updatable attributes to
// fit your needs. By the way, why here the length is 20 and not 4?
#Column(name = "product_id", length = 20, insertable=false, updatable=false)
private Integer productId;
#ManyToOne(fetch= FetchType.LAZY)
#JoinColumn(name="product_id")
private Product product;
#Column(name = "payment_methods", length = 20000)
private String paymentMethods;
}
With this setup, as you can see in the Hibernate documentation - it is for an old Hibernate version, but it is correct today, you can use fetch joins to obtain the required information:
A "fetch" join allows associations or collections of values to be initialized along with their parent objects using a single select. This is particularly useful in the case of a collection.
For your example, consider the following HQL (assume outer join semantics, modify it as appropriate):
select product
from Product as product
left join fetch product.categories
left join fetch product.paymentMethods
where product.userId = 1
This will provide you the list of products for userId 1, with all the associated references to categories and payment methods initialized.
The conversion between the entity and the DTO should be straightforward:
Session session = ...
List<Product> products = session.createQuery(
"select product " +
"from Product as product " +
" left join fetch product.categories " +
" left join fetch product.paymentMethods " +
"where product.userId = :userId", Product.class)
.setParameter( "userId", 1)
.getResultList();
List<ProductFullDTO> productFullDTOs = null;
if (products != null) {
productFullDTOs = products.stream()
.map((product -> {
ProductFullDTO productFullDTO = new ProductFullDTO();
productFullDTO.setId(product.getId());
productFullDTO.setUserId(product.getUserId());
List<ProductCategory> categories = product.getCategories();
if (categories != null) {
List<Integer> categoriesIds = categories.stream()
.map(ProductCategory::getCategoryId)
.collect(Collectors.toList())
;
productFullDTO.setCategories(categoriesIds);
}
List<ProductPaymentMethods> paymentMethods = product.getPaymentMethods();
if (paymentMethods != null) {
List<String> paymentMethodsIds = paymentMethods.stream()
.map(ProductPaymentMethods::getPaymentMethods)
.collect(Collectors.toList())
;
productFullDTO.setPaymentMethods(paymentMethodsIds);
}
return productFullDTO;
}))
.collect(Collectors.toList())
;
}
System.out.println(productFullDTOs == null ? "No products found." : productFullDTOs.size() + " products found.");
You should use TypeHandler to finish this job. I just give paymentMethods as example.
#Results({
#Result(column = "product.id", property = "id"),
#Result(column = "user_id", property = "userId"),
#Result(column = "category_id", property = "categories"),
#Result(column = "payment_methods", property = "paymentMethods" ,typeHandler= StrListTypeHandler.class),
})
#Select("SELECT * FROM Product INNER JOIN product_category ON Product.id = product_category.productId "
+ " INNER JOIN product_payment_methods ON Product.id = product_payment_methods.productId "
+ " WHERE userId = 1")
List<ProductFullDTO> getProduct();
// the below is TypeHandler implementation
#Component
public class StrListTypeHandler implements TypeHandler<List<String>> {
#Override
public void setParameter(PreparedStatement preparedStatement, int i, List<String> strings, JdbcType jdbcType) throws SQLException {
StringBuffer sb = new StringBuffer();
for (String s : strings) {
sb.append(s).append(",");
}
preparedStatement.setString(i, sb.toString().substring(0, sb.toString().length() - 1));
}
#Override
public List<String> getResult(ResultSet resultSet, String s) throws SQLException {
String[] arr = resultSet.getString(s).split(",");
return Arrays.asList(arr);
}
#Override
public List<String> getResult(ResultSet resultSet, int i) throws SQLException {
String[] arr = resultSet.getString(i).split(",");
return Arrays.asList(arr);
}
#Override
public List<String> getResult(CallableStatement callableStatement, int i) throws SQLException {
String[] arr = callableStatement.getString(i).split(",");
return Arrays.asList(arr);
}
}

Duplicate parent and child data in jpql (JPA)

I have a Production class and ProductionDetail entity class where Id of Production table is a foreignKey as production_id in ProductionDetail entity class so my both entity class with mapping has given bellow
Production Entity Class:
#Entity
#Table(name = "tbl_production")
#XmlRootElement
public class TblProduction implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 45)
#Column(name = "ID")
private String id;
#Column(name = "PRODUCTION_DATE")
#Temporal(TemporalType.DATE)
private Date productionDate;
#Column(name = "START_DATETIME")
#Temporal(TemporalType.TIMESTAMP)
private Date startDatetime;
#Column(name = "END_DATETIME")
#Temporal(TemporalType.TIMESTAMP)
private Date endDatetime;
#Size(max = 45)
#Column(name = "MACHINE_UUID")
private String machineUuid;
**Relation with Production Details Table**
#OneToMany(mappedBy = "production")
#XmlElement(name = "productionDetails")
private List<TblProductionDetail> productionDetailList;
#PrimaryKeyJoinColumn(name = "MACHINE_UUID", referencedColumnName = "UUID")
#ManyToOne(fetch = FetchType.LAZY)
private MstMachine mstMachine;
#XmlTransient
public MstMachine getMstMachine() {
return this.mstMachine;
}
}
Production Details Entity Class:
#Entity
#Table(name = "tbl_production_detail")
#XmlRootElement
public class TblProductionDetail implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 45)
#Column(name = "ID")
private String id;
#Size(max = 45)
#Column(name = "COMPONENT_ID")
private String componentId;
#Size(max = 45)
#Column(name = "PRODUCTION_ID")
private String productionId;
**Relation with Production Class**
#ManyToOne
#JoinColumn(name = "PRODUCTION_ID", referencedColumnName = "ID", insertable = false,
updatable = false)
private TblProduction production;
#Transient
public String componentCode;
#Transient
public String componentName;
#PrimaryKeyJoinColumn(name = "COMPONENT_ID", referencedColumnName = "ID")
#ManyToOne(fetch = FetchType.LAZY)
private MstComponent mstComponent;
#XmlTransient
public MstComponent getMstComponent() {
return this.mstComponent;
}
public void setMstComponent(MstComponent mstComponent) {
this.mstComponent = mstComponent;
}
}
ParentList Class:
public class TblProductionList {
private List<TblProduction> productionList;
public TblProductionList() {
productionList = new ArrayList<>();
}
public List<TblProduction> getTblProductions() {
return productionList;
}
public void setTblProductions(List<TblProduction> tblProductionList) {
this.productionList = tblProductionList;
}
}
BusinessLogic(DAO Class):
public TblProductionList getJson() {
TblProductionList response = new TblProductionList();
StringBuilder retrieveQuery = new StringBuilder();
retrieveQuery.append(" SELECT prod FROM TblProduction prod ");
retrieveQuery.append(" JOIN FETCH prod.productionDetailList ");
retrieveQuery.append(" WHERE prod.endDatetime IS NULL ");
retrieveQuery.append(" AND prod.machineUuid IS NOT NULL ");
retrieveQuery.append(" AND NOT EXISTS (SELECT tpt FROM
TblProductionThset tpt WHERE prod.id = tpt.productionId) ");
retrieveQuery.append(" AND EXISTS (SELECT mmfd FROM
MstMachineFileDef mmfd WHERE prod.machineUuid = mmfd.machineUuid
AND mmfd.hasThreshold = 1) ");
retrieveQuery.append(" ORDER BY prod.id ");
Query query =
entityManager.createQuery(retrieveQuery.toString());
List thresholdList = query.getResultList();
response.setTblProductions(thresholdList);
return response;
}
According to the database I am getting expected master child data like below
After designing this entity class I am expecting that I will get 3 master records where each record has 2 detail records. But I am getting 6 duplicate master records with 12 child records. Can anyone suggest to me please where is my code became wrong and why this situation raised? please check the JSON data that I am getting from API.
change your array list to hash set then records are not duplicate.

Deleting entity, which is element of collection and has collection of another entities in Hibernate

I have entity Ad:
#Entity
#NamedQueries({
#NamedQuery(name = "getAllAds",
query = "from Ad"),
#NamedQuery(name = "deleteById",
query = "delete from Ad where id = :id")
})
#FieldMatch(first = "initLanguage",second = "resultLanguage", message = "Languages must be different")
#Table(name = "AD_TEST")
public class Ad implements Serializable{
/**
* Version of this class in production
*/
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "standard", initialValue = 1)
#GeneratedValue(generator = "standard", strategy =GenerationType.SEQUENCE)
#Column(name = "AD_ID")
private long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "CLIENT",nullable = false)
private Client client;
#NotBlank
#Column(name = "AD_NAME", nullable = false)
private String name;
#NotBlank
#Column(name = "AD_DESC",nullable = false,length = 1000)
#Lob
#Size(min = 0, max = 1000)
private String description;
#Column(name = "AD_COUNTRY", nullable = false)
private String country;
#Column(name = "AD_CITY", nullable = false)
private String city;
#NotNull
/**
* Добавить проверку валидности даты
*/
#DateTimeFormat(iso = ISO.DATE,pattern = "dd.MM.yyyy")
#Column(name = "AD_END_DATE",nullable = false)
private LocalDate endDate;
#NotNull
#Column(name = "AD_INIT_LANGUAGE",nullable = false)
#Enumerated(EnumType.STRING)
private Language initLanguage;
#NotNull
#Column(name = "AD_RESULT_LANGUAGE",nullable = false)
#Enumerated(EnumType.STRING)
private Language resultLanguage;
#NotNull
#Column(name = "AD_TRANSLATE_TYPE",nullable = false)
#Enumerated(EnumType.STRING)
private TranslateType translateType;
#Lob
#Column(name = "AD_FILE")
private byte[] file;
#NotNull
#Column(name = "AD_COST",nullable = false,precision = 2)
private double cost;
#NotNull
#Column(name = "AD_CURRENCY",nullable = false)
#Enumerated(EnumType.STRING)
private Currency currency;
#Column(name = "AD_CREATING_DATE",nullable = false)
private LocalDateTime creationDateTime;
#Column(name = "AD_STATUS",nullable = false)
#Enumerated(EnumType.STRING)
private AdStatus status;
#OnDelete(action = OnDeleteAction.CASCADE)
#OneToMany(fetch = FetchType.EAGER,orphanRemoval = true,mappedBy = "ad")
#Cascade(CascadeType.ALL)
private List<ResponsedAd> responsedAds = new ArrayList<>();
public Ad(){}
/*Getters and setters, hashCode and equals*/
Ad is element of collection in Client:
#Entity
#NamedQueries({
#NamedQuery(name = "clientByEmail",
query = "from Client client where client.email = :email")
})
#Table(name = "CLIENT_TEST")
#PrimaryKeyJoinColumn(name= "client_id")
public class Client extends User{
/**
* Version of this class in production
*/
private static final long serialVersionUID = 1L;
#OneToMany(fetch = FetchType.EAGER,orphanRemoval = true,mappedBy = "client")
#Cascade(CascadeType.ALL)
public List<Ad> ads = new ArrayList<>();
#OneToMany(fetch = FetchType.EAGER,orphanRemoval = true,mappedBy = "client")
#Cascade(CascadeType.ALL)
private List<ResponsedAd> responsedAds = new ArrayList<>();
public Client(){}
Ad.class and Client.class have collection of ResponseAd objects:
#Entity
#Table(name = "RESPONSED_AD_TEST")
#NamedQueries({
#NamedQuery(name = "responsedAdsByAd",query="from ResponsedAd where ad = :ad")
})
#Component
public class ResponsedAd {
#Id
#SequenceGenerator(name = "standard", initialValue = 1)
#GeneratedValue(generator = "standard", strategy =GenerationType.SEQUENCE)
private long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "AD",nullable = false)
private Ad ad;
#Column(nullable = false)
private LocalDateTime dateTimeOfResponse;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "CLIENT")
private Client client;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "TRANSLATOR",nullable = false)
private Translator translator;
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private ResponsedAdStatus status;
public ResponsedAd(){}
I want that after deleting Ad object, all ResponseAd objects from collection were deleted too.
For this I have:
public void deleteById(long id){
Ad ad = get(id);
CopyOnWriteArrayList<ResponsedAd> list = new CopyOnWriteArrayList<>(ad.getResponsedAds());
list.forEach(rad->{
Translator translator = rad.getTranslator();
translator.removeResponsedAd(rad);
ad.removeResponsedAd(rad);
Client client = rad.getClient();
client.removeResponsedAd(rad);
});
Client client = ad.getClient();
client.removeAd(ad);
}
Say me please, how can I do this right? I have very a lot of relationships and when I try do this, I get error
HTTP Status 500 - Request processing failed; nested exception is
org.springframework.dao.InvalidDataAccessApiUsageException:
deleted object would be re-saved by cascade (remove deleted object from associations):
[ua.translate.model.ad.ResponsedAd#102]; nested exception is org.hibernate.ObjectDeletedException:
deleted object would be re-saved by cascade (remove deleted object from associations): [ua.translate.model.ad.ResponsedAd#102]
First of all,
You don't need to use #NotNull if you have nullable = false already declared in #Column(nullable = false).
Second,
What you're trying to do is doing operations in Cascade. Add this cascade = CascadeType.ALL or maybe cascade = CascadeType.REMOVE to your #ManyToOne tags and it should work.
Use this as a reference: JPA #ManyToOne with CascadeType.ALL
My Client object: Client client = rad.getClient(); have two responsedAd object in collection with the same id, therefore even after deleting this responsedAd object: client.removeResponsedAd(rad); client object has one more. And now main question is why this client have two objects with the same id.

JPA EntityGraph is not working; Always loading related collections

I am trying to get named entity graphs to work properly. Basically, I have a Customer who can have zero or more Addresses. When I query to get a list of Customers, I want the customer's fields, but not the associated Addresses. When I query for a particular Customer, I want its fields and all of the associated Addresses. So, I've been trying to use named entity graphs because they seem to address this situation. I created one to get basic information, and another that gets everything. Unfortunately, when I use either graph, I still always get everything. I am sure that I am making a simple mistake, but I cannot seem to find it. I'd appreciate your help to figure out my error. The relevant code follows...
Thank you for your time and advise,
Mike
/////////////////////////////////////////////////////////
// ENTITIES
/////////////////////////////////////////////////////////
#Entity
#Table(name = "customers")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c")})
#NamedEntityGraphs({
#NamedEntityGraph(
name="previewCustomerEntityGraph",
attributeNodes={
#NamedAttributeNode("id"),
#NamedAttributeNode("displayAs"),
#NamedAttributeNode("rowVersion")
}
),
#NamedEntityGraph(
name="fullCustomerEntityGraph",
attributeNodes={
#NamedAttributeNode("id"),
#NamedAttributeNode("displayAs"),
#NamedAttributeNode("rowVersion"),
#NamedAttributeNode(value = "addressCollection", subgraph = "addressCollection")
},
subgraphs = #NamedSubgraph(
name = "addressCollection",
attributeNodes = {
#NamedAttributeNode("id"),
#NamedAttributeNode("displayAs"),
#NamedAttributeNode("rowVersion")
}
)
)
})
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "Id")
#XmlAttribute(required=true)
private Long id;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 256)
#Column(name = "DisplayAs")
#XmlElement(required=true, nillable=false)
private String displayAs;
#Basic(optional = true)
#Size(max = 256)
#Column(name = "Name")
#XmlElement(required=true, nillable=true)
private String name;
#Basic(optional = false)
#NotNull
#XmlElement(required=true, nillable=false)
#Column(name = "RowVersion")
#Version
private Timestamp rowVersion;
#OneToMany(mappedBy = "customerId",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Collection<Address> addressCollection;
//#XmlTransient
public Collection<Address> getAddressCollection() {
return addressCollection;
}
public void setAddressCollection(Collection<Address> addressCollection) {
this.addressCollection = addressCollection;
}
// Object methods have been removed to save space
}
#Entity
#Table(name = "addresses")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Address.findAll", query = "SELECT a FROM Address a")})
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#XmlAttribute(required=true)
#Column(name = "Id")
private Long id;
#Basic(optional = false)
#NotNull
#Column(name = "RowVersion")
#Temporal(TemporalType.TIMESTAMP)
#XmlElement(required=true, nillable=true)
private Date rowVersion;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 256)
#Column(name = "DisplayAs")
#XmlElement(required=true, nillable=true)
private String displayAs;
#JoinColumn(name = "CustomerId", referencedColumnName = "Id")
#ManyToOne
private Customer customerId;
// Object methods have been removed to save space
}
/////////////////////////////////////////////////////////
// QUERIES
/////////////////////////////////////////////////////////
protected T find(Object id) {
// Filter to only return entities belonging to the tenant.
EntityGraph eg = getEntityManager().getEntityGraph("fullCustomerEntityGraph");
javax.persistence.criteria.CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery<T> cq = cb.createQuery(entityClass);
javax.persistence.criteria.Root<T> from = cq.from(entityClass);
javax.persistence.criteria.CriteriaQuery<T> select = cq.select(from);
cq.where(cb.and(
cb.equal(from.<T>get("id"), id)
));
javax.persistence.TypedQuery<T> tq = getEntityManager().createQuery(select);
tq.setHint("javax.persistence.fetchgraph", eg);
return getSingleResultOrNull(tq);
}
protected List<T> findAll() {
// Filter to only return entities belonging to the tenant.
EntityGraph eg = getEntityManager().getEntityGraph("previewCustomerEntityGraph");
javax.persistence.criteria.CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery<T> cq = cb.createQuery(entityClass);
javax.persistence.criteria.Root<T> from = cq.from(entityClass);
javax.persistence.criteria.CriteriaQuery<T> select = cq.select(from);
javax.persistence.TypedQuery<T> tq = getEntityManager().createQuery(select);
tq.setHint("javax.persistence.fetchgraph", eg);
return tq.getResultList();
}
protected T getSingleResultOrNull(javax.persistence.TypedQuery<T> query) {
query.setMaxResults(1);
List<T> list = query.getResultList();
if (list.isEmpty()) {
return null;
}
return list.get(0);
}
The serializer is calling getAddressCollection.
Note that #XmlTransient is commented on address list. Place this annotation back and try again.
In this case auto serialization will allways avoid the list. If you need to return the full graph somewhere else I recommend writing your own serialization logic or create a Value Object to hold exactly what you need and let serializer do the job.

JPA cascade merge does not persist attribute

I've an entity on OpenJPA 2.0
#Entity
#Table(name = "os_wfentry")
#SequenceGenerator(name = "jwe_seq", sequenceName = "jwe_seq", initialValue = 10, allocationSize = 1)
public class JPAWorkflowEntry implements WorkflowEntry, Serializable {
private static final long serialVersionUID = -755511983025049452L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jwe_seq")
private long id;
#Column(name = "name")
private String workflowName;
#Column(name = "state")
private Integer workflowState;
#Column(name = "version")
private Integer version;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "entry")
private final List<JPACurrentStep> currentSteps;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "entry")
private final List<JPAHistoryStep> historySteps;
public JPAWorkflowEntry() {
currentSteps = new ArrayList<>();
historySteps = new ArrayList<>();
}
...
and on JPACurrent and JPAHistory step I've inserted:
#ManyToOne
#Column(name = "entry_id")
protected JPAWorkflowEntry entry;
It is all correct (in theory); but when I try to save (or update) a new instance of JPAWorkflowStore, having a NOT EMPTY list of (current or history) steps, list of steps attribute is not persistend on db and it always an empty list. Can You help me?? What am I doing wrong??
You need to specify #JoinColumn(name = "id", nullable = false) for your JPACurrent and JPAHistory.
What you have done is #Column(name = "entry_id") i dont see "entry_id" mapping to any cölumn in JPAWorkflowEntry

Categories

Resources