I'm working on a big project using Glassfish 4 (JavaEE7) and EclipseLink (JPA2.1).
When I modify the value of a LOB field, this resets all the previous value of then entity.
For instance, I have the following entity:
#Entity
#Table(name="person")
public class Person implements Serializable {
#Id
#Column(name = "ID")
private Integer id;
#Column(name = "LASTNAME")
private String lastname;
#Column(name = "FIRSTNAME")
private String firstname;
#Column(name = "VERSION")
private Integer version;
#Lob
#Basic(fetch = FetchType.LAZY)
#Column(name = "REMARKS")
private String remarks;
/* getter & setters */
}
I use the following code to update my entity:
#Singleton
#TransactionManagement(TransactionManagementType.BEAN)
#Startup
public class ModifierBean implements ModifierBeanLocal {
#Resource
private UserTransaction userTransaction;
#PersistenceContext
private EntityManager entityManager;
#Override
public String modify(Integer id, String lastname, String firstname, String remarks) {
try {
StringBuilder result = new StringBuilder(256);
userTransaction.begin();
Query query = entityManager.createQuery("SELECT p FROM Person p WHERE p.id = ?1");
query.setParameter(1, id);
Person p = (Person)query.getSingleResult();
result.append("State 0: lastname=").append(p.getLastname()).append(", firstname=").append(p.getFirstname()).append(", version=").append(p.getVersion()).append("<br />");
p.setFirstname(firstname);
p.setLastname(lastname);
p.setVersion(p.getVersion()+1);
result.append("State 1: lastname=").append(p.getLastname()).append(", firstname=").append(p.getFirstname()).append(", version=").append(p.getVersion()).append("<br />");
p.setRemarks(remarks); // <= reset the other fields
result.append("State 2: lastname=").append(p.getLastname()).append(", firstname=").append(p.getFirstname()).append(", version=").append(p.getVersion()).append("<br />");
userTransaction.commit();
return result.toString();
} catch(Exception e) {
//...
}
}
}
When I'm running the following code, here is the output I get from the Bean:
State 0: lastname=Smith, firstname=John, version=0
State 1: lastname=John2, firstname=Smith2, version=1
State 2: lastname=Smith, firstname=John, version=0
As one can see, the values of then entity are reset when LOB is loaded, which is very annoying for my purpose.
There are some tricks to prevent that to happen, for instance I tested the following options:
call entityManager.flush() just before the modification of the LOB field
call p.getRemarks() juste before doing the first modifications
I also noticed that if the LOB field is not changed, then the values are correctly committed.
I know that a LAZY value is loaded on demand but I do not understand why it resets all the other values.
Is this a bug or a standard behaviour ? Is there any way to make it work as expected (no reset of value) ?
I just found out that this exact bug has been open in EclipseLink (already present since v2.1.3): https://bugs.eclipse.org/bugs/show_bug.cgi?id=371743
It would depend on your persistence unit properties and it's assocation to the transaction. I believe though, since you haven't explicietely joined the EntityManager to the transaction, the Em is behaving as if it is outside the transaction causing the following behavior to apply:
"Outside transaction entire object (fetch group members included) is refreshed in the shared cache."
http://wiki.eclipse.org/EclipseLink/Development/2.1/AdvancedJPA_Queries/FetchGroup
Try calling entityManager.joinTransaction(); after starting the transaction to see if this helps. EclipseLink
Related
have a many-1 relationship pupil-formGroup: pupils are assigned to a formGroup and a formGroup can contain many pupils. I have attempted to implement an InverseRelationShadowVariable having watched your video/tutorial https://www.youtube.com/watch?v=ENKHGBMDaCM (which does not quite correspond with the latest optaplanner documentation I realise)
FormGroup extracts
#Entity
#PlanningEntity
public class FormGroup {
#InverseRelationShadowVariable(sourceVariableName = "formGroup")
#OneToMany(mappedBy = "formGroup", fetch = FetchType.EAGER)
private List<Pupil> pupilList = new ArrayList<Pupil>();
public List<Pupil> getPupilList() {
return pupilList;
}
public Integer getPupilCount() {
return pupilList.size();
}
Pupil extracts
#Entity
#PlanningEntity
public class Pupil
#PlanningVariable(valueRangeProviderRefs = "formGroupRange")
#ManyToOne
private FormGroup formGroup;
Config extracts
<solutionClass>org.acme.optaplanner.domain.Plan</solutionClass>
<entityClass>org.acme.optaplanner.domain.Pupil</entityClass>
<entityClass>org.acme.optaplanner.domain.FormGroup</entityClass>
I believe I've followed the steps in the videoexactly (don't we all say that) but at solve time I get hundreds of errors... Repetitions of the following
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
Any hint gratefully received...
The InverseRelationShadowVariable creates a bi-directional relationship between the genuine planning entity (Pupil) and the planning value (FormGroup). This may become problematic if you re-use your planning domain classes for other purposes, such as ORM persistence or serialization.
In this case, Jackson is unable to serialize Pupil, because it references a FormGroup, which has a List containing a reference back to that Pupil. See? An endless loop.
Solve this issue by adding the #JsonIgnore annotation on top of your inverse relation property and breaking that loop for Jackson:
#Entity
#PlanningEntity
public class FormGroup {
#JsonIgnore // THIS IS THE FIX
#InverseRelationShadowVariable(sourceVariableName = "formGroup")
#OneToMany(mappedBy = "formGroup", fetch = FetchType.EAGER)
private List<Pupil> pupilList = new ArrayList<Pupil>();
public List<Pupil> getPupilList() {
return pupilList;
}
public Integer getPupilCount() {
return pupilList.size();
}
...
Suppose that I have a Hibernate entity whose fields are altered via new instance creation (e.g. Lombok builder).
Does that mean that #DynamicUpdate is not going work? Is is required to change the fields via setters in this case?
P.S. Here is an example
#Table
#Entity
#Builder(toBuilder = true)
class Person {
private String firstName;
private String lastName;
}
...
Person person = personRepository.findById(1).orElseThrow();
personRepository.saveAndFlush(
person.toBuilder()
.firstName("newName")
.build() // creates new instance
);
I am creating a web application that needs a table of notifications to display to various users. For some reason the JPQL query I've written is throwing a java.lang.IllegalArgumentException. My application already has a transaction table, structured and queried using an identical approach (afaik), which works perfectly.
I have been shuffling the code around, changing variable names and character cases for hours trying to get this to work but I'm still getting the exception every time. Does anyone have any idea where I'm going wrong?
My NotificationEntity is as follows:
#Table(name="notificationentity")
#NamedQuery(name="fetch_user_notifications", query="SELECT n FROM NotificationEntity n WHERE n.notificationrecipient=:username")
#Entity
public class NotificationEntity implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
String notificationSender;
#NotNull
String notificationrecipient;
... other fields + methods
}
The JPQL query is called from an EJB (NotificationStorageServiceBean) that implements an interface (NotificationStorageService) with the following method:
#Override
public synchronized List<NotificationEntity> getUserNotificationList(String username)
{
List notifications;
notifications = em.createNamedQuery("fetch_user_notifications").setParameter("notificationrecipient", username).getResultList();
return notifications;
}
And the EJB method is called from a CDI backing bean for my .xhtml UI, using the FacesContext's currently logged in user to provide the argument for these methods.
#EJB
NotificationStorageService notificationStore;
public List<NotificationEntity> getUserNotificationList()
{
return notificationStore.getUserNotificationList(this.currentUser);
}
The exact error I get is:
java.lang.IllegalArgumentException: You have attempted to set a parameter value using a name of notificationrecipient that does not exist in the query string SELECT n FROM NotificationEntity n WHERE n.notificationrecipient=:username.
The parameter name in a JPQL query starts with a colon. So just use
setParameter("username", username)
I have a class that goes:
#Entity
#Table(name = "TRANSACTIONS")
public class Transaction {
#Id
#Column(name = "TX_ID")
private Long id;
#Basic
#Column(name = "AMOUNT")
private Double amount;
#Column(name = "AMOUNT_COST")
private Double amountCost;
#NotNull
#Column(name = "AMOUNT_TAX")
private Double amountTax;
#NotNull
#Column(name = "BANKACCOUNT")
private String bankAccount;
//getters and setters here
}
the problem is, when I create Transaction object, and fill data, and then I want it to merge, hibernate merges it with fields AMOUNT_COST and BANKACCOUNT set to null. I checked with debugger - the object I want to merge is correctly filled with data, AMOUNT_COST is set to 0.0 and BANKACCOUNT is correct.
I have no clue what might be the problem here:
-I double checked column names
-I double checked my sql table, and types of data
-I tried to put annotations on getters, or on declaration of variable - nothing changes.
When I set columns to 'nullable' then the object is merged with null values, but only for those two fields (!). I'm clueless here, so I really need a hint. Thanks in advance.
ha. I forgot to mention that I'm using extended entity menager, and in this case, it was it's fault.
Im trying to write an aplication with uses hibernate to write to database, however in some actions i have to use JDBC on data in tables made by HB.
JDBS is requred to give administrator ability to create SQL queries with will return statistic info about data in database like number of processed document of specified type, numbers of success/failed log in attempts or total value of products in orders.
To do that i've done an from that allows to create class that has override toString() with return nice sql query string.
All works but now im trying to make administrator live easier by hiving him an ability to choose of table/column names. And here is an problem, because they are created by hibernate. some by #column annotation other by field name.
How can i check how field mapping?
I know its all about reflections but didnt do much of that in java yet.
example
#Entity
#Table(name= "my_table_name" )
public class TableOFSomething implements Serializable{
//This field isn't mapped into database and info about it is not requred.
//In fact, info about it may cause an error.
private static final long serialVersionUID = 7L;
#Id
#Column(name="id")
private String id;
private String fieldOne;
#Column(name="field_two")
private String fieldTwo;
#Column(name="renamed_just_for_fun")
private int Number;
//code with getters & setters
}
How to write methods that will have definition like
public <T> String tableName(Class<T> Target); //returns name of table in database
public <T> ArrayList<String> tabelFields(Class<T> Target); //returns name of fields in database
Hibernate has API - getClassMetadata that can explore the mapping. The API might change and is now located in another place , but i will use it and not in reflection for this.
look on this post for more details:
Get the table name from the model in Hibernate
if you want reflection , so use this link
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import javax.persistence.Column;
import javax.persistence.Table;
import odi.beans.statistic.QueryBean;
public class ReflectionTest {
public static void main(String[] args) {
ReflectionTest test=new ReflectionTest();
System.out.println("Table name of "+QueryBean.class.getName()+" is "+test.getTableName(QueryBean.class));
System.out.println("Column names in this table are:");
for(String n: test.getColumnNames(QueryBean.class)){
System.out.println("\t"+n);
}
System.out.println("Good bye ;)");
}
public <T> ArrayList<String> getColumnNames(Class<T> target) {
ArrayList<String> ret=new ArrayList<>();
Field[] fields = target.getDeclaredFields();
String fieldName =null;
for (Field f : fields) {
//jump to next if if field is static
if (Modifier.isStatic(f.getModifiers()))
continue;
if (f.isAnnotationPresent(Column.class)) {
Column a = f.getAnnotation(Column.class);
fieldName = a.name();
} else {
fieldName = f.getName();
}
ret.add(fieldName);
}
return ret;
}
public <T> String getTableName(Class<T> target){
String ret=target.getSimpleName();
if (target.isAnnotationPresent(Table.class))
{
Table t=target.getAnnotation(Table.class);
ret=t.name();
}
return ret;
}
}
Is it cover all possibilities?
I know now that Hibernate way would be easier, but this is also about learning of very useful reflection mechanism :)
EDIT:
Important question:
Will this work only on annotations or also on xml mapping?