are calculated, thus read only fields, so "setInteresting" and "setFullName" don't exist.
In Create or Edit, fields Interesting and FullName are present, so the Create action fails. Commenting them out in the Create and Edit forms resolved the issue.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I don't hate you, I am actually excited somebody runs into these issues. As I said, I mostly test with reverse-engineered entity classes, and so most, if not all of those entities have read-write access fields.
I will say, though, that you can also use the @Transient annotation. Those fields get skipped by the wizard. But let me see if there's anything I can find to detect a read-only situation.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
So let me throw this question back at you? What should happen with these fields? Should they be displayed in the forms, just not as input fields? Or are they more for internal purposes and should be skipped altogether?
I can't find any code for detecting read-only fields inside NetBeans' JpaControllerUtil class, a helper class that's being used by the wizard when making many determinations about an entity.
Last edit: Kay Wrobel 2013-11-08
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Next question: if the field is indeed read-only, is it an actual database field or is it a field calculated at runtime? If it is an actual database field, how would it be populated if you make it a read-only field?
Let's take your example for getFullName: this is actually something that I believe would not exist as a field inside your database table, but as an internal field. That method should definitely by annotated @Transient in my opinion. But say, you have a field that comes out of a database, maybe it is being calculated by a trigger procedure inside the database. Could or should it still be allowed to be modified? Then the field would not actually be read-only but an ordinary read-write field.
So let's say you make it @Transient. The wizard currently skips such fields. If you still wanted to generate a display field for @Transient fields, I would have to add some code to not skip them but pass them along somehow.
What's your thought? I need some direction on this topic from you and maybe other users.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
So this is interesting. I can't actually put the @Transient annotation on the method without the IDE complaining about field access and that I should unify it. So forget that for the moment. Yes, I actually created a read-only "field" like your getFullName. My persistence unit was set to create the database table for me in MySQL. And so what I found is that JPA actually didn't try to create any table field for FullName. That is good news for me since I didn't know how this situation is being handled.
Alright, so now I have to find a way of detecting read-only fields. They're really properties then since no actual fullName field exists in the entity.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi! Sorry for disappearing! Do you still need my answers?
So let me throw this question back at you? What should happen with these fields? Should they be displayed in the forms, just not as input fields? Or are they more for internal purposes and should be skipped altogether?
That's a good question... I would say "It depends". Some getters like getFullName could be useful if they were displayed, some other fields would be better to hide them. E.g. let's say that I have
this is definitely a field that I wouldn't show because it would take a long time to get the answer. Maybe I would too hide properties that could be confusing to the user.
Next question: if the field is indeed read-only, is it an actual database field or is it a field calculated at runtime? If it is an actual database field, how would it be populated if you make it a read-only field?
Usually it's a calculated field, but it could be a database field too. E.g.:
publicclassCustomer{@NotNullprivateStringname;@NotNullprivateStringsurname;@NotNullprivatestringfullName;publicvoidsetName(Stringname){this.name=name}publicStringgetName(){returnname;}publicvoidsetSurname(Stringsurname){this.surname=surname}publicStringgetSurname(){returnsurname;}/* * NOTE: only getter for fullName! */publicStringgetFullName(){returnfullName}/* * fullName is a calculated field, but I'm persisting it * because in my Application I need to do a lot of * queries/joins/whatever using this field. * Calculating it at runtime could lead to * performance issues */@PrePersist@PreUpdateprivateprePersist(){fullName=this.name+" "+this.surname;}
I hope that this example clarifies the point. Both persisted readonly field and non persisted readonly fields are possible. Please note that I wrote this code example directly in the reply windows, so it could have some errors since it isn't tested.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks. So let's just keep it simple: if there's only a getter method without a setter method, it's read-only and should be displayed only. I have started work with this assumption in mind. I am currently running into issues with the templates. Here's why: if it's a read-only field, I want to apply the same template as in the View.xhtml file. But I don't want to duplicate the same template code in two places. So I was thinking of centralizing the template code for one field into a separate template and have that be included in both View.xhtml and Edit.xhtml, skip it in Create.xhtml since the record doesn't exist yet. Unfortunately, the <#include> statement doesn't find my template file. So that's where I'm at right now. Will continue working on this on Monday.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Kay while you are working on this I have something to add. JPA allows you to add the @Version annotation to the Entity class. This allows you to use optimistic locking on your database. In certain cases using this makes sense.
When using this annotation on the example below the version number is still editable. Editing the version number will cause a exception to be thrown. Also as the version number automatically increases you will need to a way to refresh the record. I provided some sample code for you to work with.
~~~~~~~~~~~~~~~~~~~~~~~~
/
* To change this template, choose Tools | Templates
* and open the template in the editor. /
package com.test.jpa;
Could you two please do me a favor and check out the source code from GIT and test the new functionality? I added capability for detecting version and read-only fields. I also added new templates that handle view and edit of an individual field so I can reuse the view-related template inside Edit.xhtml. Please make sure you don't have any custom-edited templates laying around in your home directories (usually under $HOME/.netbeans/7.4/config/Templates).
I am looking forward to your feedback.
Neil: I had tried to replicate a @Version field in my entity and ran into issues editing (not creating) records. I don't have much experience with version fields and depend on your expertise on that.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
Anonymous
-
2013-11-11
Hello Kay!
Thank you again for your quick support! I'll try tomorrow!
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The error you are getting is due to the fact that when you edit the record the version number in the grid does not get updated. So when you edit it a second time the old version number gets posted thus throwing a optimistic locking exception like the following:
Since this method is called before the edit the list is not null and thus does not get refreshed. Here is the bit you need to understand. Since we are not editing the version column but only the name of the customer the new name is placed into the grid as we are modifying the object in the list. However the version number is incremented on the back end and the list does not read from the database again. Thus while the name of the customer changes on the grid list it does not change the version number as we have not changed it.
I have a workaround but it is not pretty. In the persist method of the AbstractController when the list gets edited set the list to null and then this will force the list to be reloaded from the database. See code below:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private void persist(PersistAction persistAction, String successMessage) {
if (selected != null) {
this.setEmbeddableKeys();
try {
if (persistAction != PersistAction.DELETE) {
this.ejbFacade.edit(selected);
/
Forcing the items to be null here ensures that we
reload the list from the database. This will read the
new version number from the database and ensure optimistic locking
works. /
On a side note I have found a generic way to implement lazy loading of all datatables. If you want I can setup a example project and send it to you so you can study it and maybe contribute the code to the project. This will then by default lazy load all of the tables and yes filtering and sorting still works.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi Neil. I'm testing this whole version situation again. I see what you're doing, but there might be a more elegant solution. One thing I'm investigating is the AbstractFacade class. The edit method does not return anything and, unfortunately, that code is generated by a piece of code that is not available to me. But, I've modified it for testing purposes. So here is the change I've made to AbstractFacade in my test project:
I've set the debugger to stop inside the facade's edit method to see what gets returned from the back-end. And on the first go around when a new entity gets created, I get an object that has version set to 1. However, when I dig inside the database for that record, version is actually NULL. Do you have an idea of what is going on?
I've created a Github for this project. Could you please take a look at it, Neil?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Neil, I looked at the entity class in my project, and removed the following line from the version field:
@Column(insertable=false,updatable=true)
That change took care of managing the version number properly. JPA now inserts 1 when the object gets created. And editing the entity also works like a charm now. I have not tested the change with the standard facade, the one that doesn't return the merged entity back to the controller. I will try that next.
In the mean time, I have updated the test project on Github to reflect the change.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hm. That kind of begs the question if an AJAX-ified GUI is even right for when you're dealing with version fields. So, I take it that what is really lacking in my design is the refresh of entities before editing. Is that correct? So maybe, just the entity itself should get refreshed prior to the edit, not the whole list? Just thinking out loud with my limited knowledge.
Regarding lazy loading and a generic approach, I am very interested in seeing a solution for that since a few users have pinged me regarding that and I had brushed it off as not really possible with the ueber-generic facade we have here. So fire away. Maybe you can ppost your solution on Github.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I just need to clean up the code slightly. It took me a solid two weeks of googling the problem and trying different things to get the generic method working. I used it in a project with over 100 tables which are very large so needed the lazy loading. At this point the code just needs a little refactoring and more comments to get it readable.
Will have it done over the weekend and send probably blog about it.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I just tried the new version. Editing and creating entities works nice, but after editing is done, the readonly fields aren't refreshed (the other fields are correctly refreshed), so I have to refresh the entire page in order to see the new value
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi Schifazl. Could you please have a look at the change I have made to the AbstractFacade and AbstractController classes? See discussion with Neil above and a link to a test project. It seems that with that change, the read-only field also gets updated properly. It is something you would have to do manually after generating the code.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I apologize for the delay in getting code to you on the Lazy Data loading I am currently travelling between cities and have been in hotels without my code available. Will be home this weekend and send you what I have done.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I've found another bug... do you hate me? :D
Read only fields are included in the Create and Edit forms.
e.g.:
or:
~~~~~~
public String getFullName(){
return this.name + " " + this.surname;
}
are calculated, thus read only fields, so "setInteresting" and "setFullName" don't exist.
In Create or Edit, fields Interesting and FullName are present, so the Create action fails. Commenting them out in the Create and Edit forms resolved the issue.
I don't hate you, I am actually excited somebody runs into these issues. As I said, I mostly test with reverse-engineered entity classes, and so most, if not all of those entities have read-write access fields.
I will say, though, that you can also use the
@Transientannotation. Those fields get skipped by the wizard. But let me see if there's anything I can find to detect a read-only situation.So let me throw this question back at you? What should happen with these fields? Should they be displayed in the forms, just not as input fields? Or are they more for internal purposes and should be skipped altogether?
I can't find any code for detecting read-only fields inside NetBeans'
JpaControllerUtilclass, a helper class that's being used by the wizard when making many determinations about an entity.Last edit: Kay Wrobel 2013-11-08
Next question: if the field is indeed read-only, is it an actual database field or is it a field calculated at runtime? If it is an actual database field, how would it be populated if you make it a read-only field?
Let's take your example for
getFullName: this is actually something that I believe would not exist as a field inside your database table, but as an internal field. That method should definitely by annotated@Transientin my opinion. But say, you have a field that comes out of a database, maybe it is being calculated by a trigger procedure inside the database. Could or should it still be allowed to be modified? Then the field would not actually be read-only but an ordinary read-write field.So let's say you make it
@Transient. The wizard currently skips such fields. If you still wanted to generate a display field for@Transientfields, I would have to add some code to not skip them but pass them along somehow.What's your thought? I need some direction on this topic from you and maybe other users.
So this is interesting. I can't actually put the
@Transientannotation on the method without the IDE complaining about field access and that I should unify it. So forget that for the moment. Yes, I actually created a read-only "field" like yourgetFullName. My persistence unit was set to create the database table for me in MySQL. And so what I found is that JPA actually didn't try to create any table field forFullName. That is good news for me since I didn't know how this situation is being handled.Alright, so now I have to find a way of detecting read-only fields. They're really properties then since no actual
fullNamefield exists in the entity.Hi! Sorry for disappearing! Do you still need my answers?
That's a good question... I would say "It depends". Some getters like getFullName could be useful if they were displayed, some other fields would be better to hide them. E.g. let's say that I have
this is definitely a field that I wouldn't show because it would take a long time to get the answer. Maybe I would too hide properties that could be confusing to the user.
Usually it's a calculated field, but it could be a database field too. E.g.:
I hope that this example clarifies the point. Both persisted readonly field and non persisted readonly fields are possible. Please note that I wrote this code example directly in the reply windows, so it could have some errors since it isn't tested.
Thanks. So let's just keep it simple: if there's only a getter method without a setter method, it's read-only and should be displayed only. I have started work with this assumption in mind. I am currently running into issues with the templates. Here's why: if it's a read-only field, I want to apply the same template as in the
View.xhtmlfile. But I don't want to duplicate the same template code in two places. So I was thinking of centralizing the template code for one field into a separate template and have that be included in bothView.xhtmlandEdit.xhtml, skip it inCreate.xhtmlsince the record doesn't exist yet. Unfortunately, the<#include>statement doesn't find my template file. So that's where I'm at right now. Will continue working on this on Monday.Kay while you are working on this I have something to add. JPA allows you to add the @Version annotation to the Entity class. This allows you to use optimistic locking on your database. In certain cases using this makes sense.
When using this annotation on the example below the version number is still editable. Editing the version number will cause a exception to be thrown. Also as the version number automatically increases you will need to a way to refresh the record. I provided some sample code for you to work with.
~~~~~~~~~~~~~~~~~~~~~~~~
/
* To change this template, choose Tools | Templates
* and open the template in the editor.
/
package com.test.jpa;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
/*
*
* @author adm_neil
/
@Entity
@Table(name = "customer")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c"),
@NamedQuery(name = "Customer.findByCustomerID", query = "SELECT c FROM Customer c WHERE c.customerID = :customerID"),
@NamedQuery(name = "Customer.findByCustomerName", query = "SELECT c FROM Customer c WHERE c.customerName = :customerName"),
@NamedQuery(name = "Customer.findByVersion", query = "SELECT c FROM Customer c WHERE c.version = :version")})
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "Customer_ID")
private Long customerID;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 64)
@Column(name = "Customer_Name")
private String customerName;
@Version
@Basic(optional = false)
@NotNull
@Column(name = "Version", insertable = false ,updatable = true)
private int version;
}
~~~~~~~~~~~~~~~~~
Hi Neil. How do you propose this should be handled? Skip the
@Versionfield or display it, similar to Schifazl's read-only fields?Neil and Schifazl:
Could you two please do me a favor and check out the source code from GIT and test the new functionality? I added capability for detecting version and read-only fields. I also added new templates that handle view and edit of an individual field so I can reuse the view-related template inside
Edit.xhtml. Please make sure you don't have any custom-edited templates laying around in your home directories (usually under $HOME/.netbeans/7.4/config/Templates).I am looking forward to your feedback.
Neil: I had tried to replicate a
@Versionfield in my entity and ran into issues editing (not creating) records. I don't have much experience with version fields and depend on your expertise on that.Hello Kay!
Thank you again for your quick support! I'll try tomorrow!
Kay
The error you are getting is due to the fact that when you edit the record the version number in the grid does not get updated. So when you edit it a second time the old version number gets posted thus throwing a optimistic locking exception like the following:
Since this method is called before the edit the list is not null and thus does not get refreshed. Here is the bit you need to understand. Since we are not editing the version column but only the name of the customer the new name is placed into the grid as we are modifying the object in the list. However the version number is incremented on the back end and the list does not read from the database again. Thus while the name of the customer changes on the grid list it does not change the version number as we have not changed it.
I have a workaround but it is not pretty. In the persist method of the AbstractController when the list gets edited set the list to null and then this will force the list to be reloaded from the database. See code below:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private void persist(PersistAction persistAction, String successMessage) {
if (selected != null) {
this.setEmbeddableKeys();
try {
if (persistAction != PersistAction.DELETE) {
this.ejbFacade.edit(selected);
/
Forcing the items to be null here ensures that we
reload the list from the database. This will read the
new version number from the database and ensure optimistic locking
works.
/
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
On a side note I have found a generic way to implement lazy loading of all datatables. If you want I can setup a example project and send it to you so you can study it and maybe contribute the code to the project. This will then by default lazy load all of the tables and yes filtering and sorting still works.
Hi Neil. I'm testing this whole version situation again. I see what you're doing, but there might be a more elegant solution. One thing I'm investigating is the
AbstractFacadeclass. Theeditmethod does not return anything and, unfortunately, that code is generated by a piece of code that is not available to me. But, I've modified it for testing purposes. So here is the change I've made toAbstractFacadein my test project:And in the AbstractController, I've changed the persist method as follows:
I've set the debugger to stop inside the facade's
editmethod to see what gets returned from the back-end. And on the first go around when a new entity gets created, I get an object that hasversionset to1. However, when I dig inside the database for that record,versionis actuallyNULL. Do you have an idea of what is going on?I've created a Github for this project. Could you please take a look at it, Neil?
Neil, I looked at the entity class in my project, and removed the following line from the version field:
That change took care of managing the version number properly. JPA now inserts
1when the object gets created. And editing the entity also works like a charm now. I have not tested the change with the standard facade, the one that doesn't return the merged entity back to the controller. I will try that next.In the mean time, I have updated the test project on Github to reflect the change.
Hm. That kind of begs the question if an AJAX-ified GUI is even right for when you're dealing with version fields. So, I take it that what is really lacking in my design is the refresh of entities before editing. Is that correct? So maybe, just the entity itself should get refreshed prior to the edit, not the whole list? Just thinking out loud with my limited knowledge.
Regarding lazy loading and a generic approach, I am very interested in seeing a solution for that since a few users have pinged me regarding that and I had brushed it off as not really possible with the ueber-generic facade we have here. So fire away. Maybe you can ppost your solution on Github.
Hi Neil Franken,
Kay
I just need to clean up the code slightly. It took me a solid two weeks of googling the problem and trying different things to get the generic method working. I used it in a project with over 100 tables which are very large so needed the lazy loading. At this point the code just needs a little refactoring and more comments to get it readable.
Will have it done over the weekend and send probably blog about it.
Hi Neil. How are you doing on this topic?
Thank you, Neil.
I'm back! Sorry for the delay, I was quite busy.
I just tried the new version. Editing and creating entities works nice, but after editing is done, the readonly fields aren't refreshed (the other fields are correctly refreshed), so I have to refresh the entire page in order to see the new value
Hm. So after you save your edit, the read-only field still contains the same content?
The field in the database gets normally updated, it's just a display issue
Hi Schifazl. Could you please have a look at the change I have made to the
AbstractFacadeandAbstractControllerclasses? See discussion with Neil above and a link to a test project. It seems that with that change, the read-only field also gets updated properly. It is something you would have to do manually after generating the code.Kay
I apologize for the delay in getting code to you on the Lazy Data loading I am currently travelling between cities and have been in hotels without my code available. Will be home this weekend and send you what I have done.
Hi Neil Franken,