This week's book giveaways are in the Java EE and JavaScript forums.
We're giving away four copies each of The Java EE 7 Tutorial Volume 1 or Volume 2(winners choice) and jQuery UI in Action and have the authors on-line!
See this thread and this one for details.
The moose likes JSF and the fly likes Problems using merge method to update DB record Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of The Java EE 7 Tutorial Volume 1 or Volume 2 this week in the Java EE forum
or jQuery UI in Action in the JavaScript forum!
JavaRanch » Java Forums » Java » JSF
Bookmark "Problems using merge method to update DB record" Watch "Problems using merge method to update DB record" New topic
Author

Problems using merge method to update DB record

Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

So I have two related tables - Country (Master) Projects (Detail). Country has a OneToMany relationship with Projects. I put a link on my AllProjects.xhtml page. When tyhe link is clicked it uses a data model class to to display the current (clicked) project record (projectRecord object). After editing all the details, the UPDATE button calls a merge method in the DAO class. On the first attempt to update, nothing happens and the record is returned as it is with no errors. If I try updating again, I get:



My classes are:

projects.java




AllProjects.java (backing bean)



the result page (AllProjects.xhtml)



Is the 'new' keyword in the entity class causing the Inser statement in the DAO because the JPA thinks I want to create a new object therefore invokes INSERT instead of UPDATE? Please let me know if anymore details are needed. Thanks in advance for any advice as to what might be causing this error!
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

I have serious doubts about your "binding" and UIModel code. The binding attribute is generally only useful when you are manually modifying the View Component Tree, and that's not something you should be doing in most cases.

The way I recommend for determining the row selected when an action is fired, whether for ordinary submit action or an AJAX event, is to wrap the dataTable's model data in an appropriate javax.faces.model object such as ListDataModel and reference that object for the dataTable "value=" property.

If you do that, then the DataModel object will track the rows properly (unless you're using Request Scope) and your dependence on JSF-specific code and JSF internals will be kept to a minimum. The application logic will be simpler, more portable, and easier to unit-test offline. Also, once wrapped, changes to the List content do not need JSF to construct a new wrapper (DataModel) or update the current one. So only if you replace the List with an entirely different List object do you have to re-wrap.


Customer surveys are for companies who didn't pay proper attention to begin with.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

OK but I'm not able to render any values using ListDataModel. My backing bean is:





This bean is used in the followng jsf page:




But the record returned is blank. Why is that ? Thanks!
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

As far as I can tell, you're constructing the the ListDataModel, but there's no code actually using it to wrap a List of EJBs for display. So it displays what's in it, which is nothing.

You do have a projectService.findAllProjects() method call, but it doesn't return anything, and if it's supposed to be doing the wrapping, it's expecting to know more about the backing bean's internals than would be generally advisable.

In short, I code stuff like more like this:

Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

Thanks the table is now populating and my code looks like this:




but I have a couple of questions:

1) You;ve used a 'private' access modifier for the DataModel - does this mean you'd write corresponding getters and setters? I actually used public modifier and it works fine
2) On line 13 of your code buildProjectList of type DataModel and projectList is a ListDataModel. So don't you need to type cast? (I got incompatibility error when i tried without casting)
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

The actual dataModel object is a Javabean property and good practice is that the actual properties are private, accessed via get/set methods. But protected, package and public scopes do work.

I think your casting problem was that I got sloppy and didn't make the DataModel a ListDataModel<Project>, just a generic ListDataModel. ListDataModel is a subclass of DataModel, so as long as the shifting is one in the proper direction, casting isn't necessary. The actual classtype expected by the dataTable is simply DataModel, so that you can use the same View Template objects for tables modelled by arrays, Lists and other ordered data sources. You could make it ListDataModel all the way, and some do, but I like the extra flexability.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

Well I managed to use the ListDataModel to pass the selected Country record into an update form using a prepareUpdate method in the backing bean. When pushing the UPDATE button and calling the actual updateCountry method I get:




The backing bean (BBCountry.java) is:



This is the jsf page UpdateCountry which uses the above backing bean to retrieve the selected record and use this is to call the updateCountry method which invokes the merge method in the DAO.


Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

Shouldn't value="#{updateCountry.newRecord.countryName}" actually be value="#{countryBB.updateCountry.newRecord.countryName}"?

As it stands, you're telling JSF to look for a backing bean named "updateCountry".
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

Sorry maybe the edits on my xhtml page didnt come through? The value I currently have is:



So I'm telling JSF to look for a backing bean named CountryBB. If I set the value to CountryBB.UpdateCountry.newRecord.countryName I get a PropertyNotFoundException - the class does not have a property named UpdateCountry.


After restoring the value (to countryBB.newRecord.countryName) another thing I tried is to initialize the newRecord as a new object in the backing bean declaration. Instead of


I used:



This causes an EJBtransactionRollBackException: Object null is not a known entity type - referring to the line in the DAO implementation class which implements the merge method


So could the original TargetUnreachable error be caused by how the newRecord object is being initialized in the backing bean or should I go back to the drawing board and look at how I'm delcaring the values in the XHTML page?






Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

CountryBB and countryBB are not the same thing.

CountryBB is the (unqualified) name of the class. But countryBB would be the default name of a single instance of class CountryBB automatically constructed by JSF on demand.

"updateCountry" in the samples you have provided is not a property, it is a JSF action method, and as such could not have sub-properties, since sub-properties (newRecord.countryName) can only exist under a property. Such as "newRecord".
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

I have constantly referred to countryBB in my jsf page UpdateCountyry which is the instance of CountryBB bean. updateCountry is also referred to on the jsf UpdateCountry page to be an action action method which is called when the UPDATE button is pressed on the jsf UpdateCountryPage.

I never intended to use CountryBB in this jsf page. I always used countryBB. Is there any place where I have used CountryBB not countryBB? Should I be using the class reference (CountryBB) instead of the instance reference (countryBB) in the UpdateCountry jsf page? I also never intended to use updateCountry as a property but instead to use it as an action. Is there any reason I should use updateCountry as a property and not an action? Can I not achieve an updated record by using newRecord as the property and updateCountry as the action, in the same way as I have already done in my jsf UpdateCountryPage?
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20


If I set the value to CountryBB.UpdateCountry.newRecord.countryName I get a PropertyNotFoundException


This could just be mis-typed, but if that was the actual "value=" value on the command button, it would be incorrect because CountryBB is not the instance name. And because updateCountry is not a property name.

In answer to your final question, you're doing it right in concept, but apparently missing something in the actuality and whatever it is isn't making the trip to where I can see it. No, there is no reason why updateCountry should be a property, but the above sample you provided is using it that way. Except at as a property, it would be coded as "countryBB.updateCountry.newRecord.countryName", not UpdateCountry. Again, because of the JavaBean capitalization conventions, which in this particular case, are probably not something that can be violated.

I make these comments because while you may have everything typed correctly in the application but are mis-typing it as your examples here on the forum, there's no way for me to determine whether that is the case or if the original code itself is incorrect. And presumably something in the code is incorrect or you wouldn't have this problem!
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

Incidentally, experience has taught me that complex software problems rarely take as long to solve as I estimate they will. But you can lose days and days over trivial things like incorrect punctuation or capitalization. Partly because you "see" what should be there and not what actually is there. It's why getting someone else to take a quick look often helps even if they're not especially expert themselves.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

Very useful words of wisdom! I'm having another look to make sure what I have typed in this sample is consistent with what's in the IDE letter by letter.

After having another look I can confirm that the syntax and naming convention in my code has been copied and pasted from my IDE (Netbeans). As far as I can tell, the code samples are totally consistent with the code on the IDE. I'm really sorry if I mispelled the object names in my explanations. I haven't used the unqualified class name in the jsf page EL statements to call the update method. I've always used the instance of the countryBB class. This means:

If I set the value to CountryBB.UpdateCountry.newRecord.countryName I get a PropertyNotFoundException


should be:

If I set the value to countryBB.updateCountry.newRecord.countryName I get a PropertyNotFoundException


Sorry for the misunderstanding. If it's clearer now I'd like to return to looking at what's missing in my code. It seems to me that my code is fine up until the prepare edit method completes its work and the selected record is rendered in the UpdateCountry jsf page. The problem comes when trying to process the adjusted values in the UpdateCountry jsf page by calling the updateCountry method in the CountryBB backing bean.

One of the possible causes of the error that comes to mind is the getter and setter methods in the backing bean for the newRecord values?



This getter statement seems to work fine when selecting the record to edits from the ListDataModel, but could this be causing a null value to be passed for newRecord when it comes to actually using updateCountry method? I tried to change the setter to the following (which resulted in a NullPointerExcepotion):



The resulting NullPointerException referred to the lines in astrix in the above code. So I guess my question is, do the getter and setter methods need to be modified to allow the selected record (newRecord) to be succesfully processed by the updateCountry method?

Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

I'm having some trouble following all this, between it being too extensive to fit on one screen and losing track of what changes where done where, so let be back off to the abstract and describe how I do it.

I'm assuming that you have 2 Views: a table list view and a detail record edit view. Each row in the table has a command button or command link that, when clicked, will display the detail record edit view for that row in the table.

To accomplish that, I would have a backing bean with Session Scope (View scope doesn't work here because there are 2 different Views). This bean would contain/construct a DataModel object (countryList) to wrap the rows being displayed.

If the action method that gets invoked when you click the commandlink on the table row is named "prepareUpdate" and the row detail data to be edited is to be referenced by a backing bean property named "editCountry" (for example), then prepareUpdate would look like this:



If the displayed table row model isn't a complete database record (for example, if you built a summary), you could extract the key fields from selectedRow, use them to fetch the complete record, and set that to editCountry. You do NOT need any parameters on the View to tell JSF which row you want to edit, since the cursor in the DataModel already knows what row it is.

On the detail edit form, your field references would be coded in the following manner:


The bbCountry saveEdits() method would then persist the edited country out to the database in the usual way. After which, you update the table datamodel, if necessary. Usually, I zap the datamodel to null so that on the next display request, the makeCountryList method gets called, and it in turn calls loadCountryList, so that an updated set of rows gets pulled from the database and wrapped in a fresh model. That assures that the new display will included changes from other users/applications. For the simpler case where the selected row is the edited row, you don't have to do anything, since changes to the wrapped data don't require creating a new DataModel.

The cancelEdit method generally does nothing, but it can null out the editCountry property if you like to keep things tidy. The main thing it does it navigate back to the table View.

On the table View, a "New Country" commandbutton would do a variant of what prepareUpdate does, except that instead of selecting a row, it creates a new Country record and initializes it. Typically, newRecord/updateRecord methods in my apps also set/clear a "save/update" flag so that the saveEdits method will know whether to do a save or an update and also ensure that the newly-created row gets inserted into the model data list wrapped by the countryList object.

Note that using this technique the only JSF-specific code required is the countryList DataModel object itself. Everything else is POJO and there's no need to reference the FacesContext.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

I'm happy to use your example and adapt it to my application environment.

So, my prepare update method looks like this:



The prepareUpdate works fine and renders the selected record in the UpdateCountry jsf page. The problem happens after I modify the values in the jsf page and try to call the following update method:



If I had orginally set the newRecord property normally like this:



The updateCountry method produces a target unreachable error ('null returned null'). If I set newRecord using the new keyword:



The update method works BUT it creates a new record instead of updatint the existing one. In brief, the problem is in the updateCountry method. I'm succesful in selecting the row, but not in processing the selected value to the merge method. Does that make sense?
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

Not entirely. You're using this.newRecord and this.(implied)foundRecord for what appears the same thing and that confuses me. I made a distinction in my example to illustrate the potential difference between a UI Model object and an ORM model object, but as I read it, both newRecord and foundRecord are the same ORM Model object (EJB).

Going way, way back to the original question, though, once you have something like what I described, there are several ORM-related questions. First, why is the "update" method attempting to do an INSERT? Secondly, what's with all the org.eclipse classes on your stack? Eclipse is an IDE and I don't know of any reason why parts of it should have been pulled into your webapp.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

as I read it, both newRecord and foundRecord are the same ORM Model object


Yes they are. I ammend the code as follows to reflect that:



the error is:

/UpdateCountry.xhtml @12,83 value="#{countryBB.newRecord.countryName}": Target Unreachable, 'null' returned null


Basicaly the same error is produced whether I use a UI MOdel or the ORM model. Going back to the original question:

First, why is the "update" method attempting to do an INSERT?


If I understand your question correctly, the update is doing an insert in order to 'complete' the updated record into the database. To be more specific it's using a merge method in a DAO class which looks like the following, I'm not sure why it's doing an insert since I'm calling merge using the Entity Manager:



So this is not an INSERT as such but a merge.

Secondly, what's with all the org.eclipse classes on your stack?


org.eclipse I think is the JPA implementation / library that I'm using. My IDE is Netbeans on the GlassFish Server. Please let me know if this answers your questions or you need me to provide more info


Dieter Quickfend
Bartender

Joined: Aug 06, 2010
Posts: 542
    
    4

(EclipseLink, JPA reference implementation)


Oracle Certified Professional: Java SE 6 Programmer && Oracle Certified Expert: (JEE 6 Web Component Developer && JEE 6 EJB Developer)
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

OK.


If I'm not mistaken, this message indicates that countryBB is null. Ordinarily that would be almost impossible given what you have, but I suspect that your use of ConversationScoped may be making it possible. It's change countryBB to SessionScoped until you have everything else working. If you don't formally start and end a Conversation, I think the net effect otherwise is going to be about the same thing as RequestScoped, which is almost entirely useless in JSF.

One thing you should be careful of when using JPA. The merge() method returns an object which may or may not be the same object as the one you passed in. Depending on context and implementation, an entirely new object may be returned. So for maximum safety, pass back the results of merge and use them as a replacement for the original property. Failure to do so may cause data out-of-sync errors.

The other "gotcha" for JPA/EJB is that you should be sure that each defined entity implements a hash and equals method such that two beans with the same primary key values always hash and compare equals. The default implementations of these methods don't ensure that, and any implementation that considers two EJBs to be unequal because a non-key value differs even though the keys are the same will behave improperly.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

It's change countryBB to SessionScoped until you have everything else working


If I change to SessionScoped the newly created records come up as blank. In other words I'm not even able to create any new records onthe DB. I have two options for using SessionScoped:

1) Use a totally different backing bean for the update method (which uses SessionScoped) and leave the current bean using ConversationScoped for all other operations

2) Change the persistence.xml property from DROP AND CREATE, create all my records firstusing ConversationScoped, change to SessionScoped, then restart the appication and test the update methods. I think that's quite a messy way of doing things and would rather avoid it if I can.

Besides that I can't think of why SessionScoped does not allow me to create new records, which means I don't even reach the point of testing if the update works.


One thing you should be careful of when using JPA. The merge() method returns an object which may or may not be the same object as the one you passed in. Depending on context and implementation, an entirely new object may be returned. So for maximum safety, pass back the results of merge and use them as a replacement for the original property.


Where do you suggest I should pass back to? In the current update method I have:


I tried passing it back like this:



But this did not work. Would appreciate your suggestions on the best way to pass back the merge results.

each defined entity implements a hash and equals method

Not sure what you mean here. Do you mean the entity defined in the domain package class? Or the way the entity is defined by the EL in the jsf pages?
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

ConversationScoped backing beans are created and destroyed over the length of the conversation. Which, unless you explicity override appears to be basically a single Request. SessionScoped beans are created and never destroyed until the HTTPSession is destroyed or they are brute-force removed from the HttpSession object. So SessionScope is the lowest-common denominator and if you cannot get things to work there, then ConversationScope isn't likely to work properly either. You can alway adjust for Conversation context once you have Session Scope working, but debugging both at the same time is asking for trouble.


One thing you should be careful of when using JPA. The merge() method returns an object which may or may not be the same object as the one you passed in. Depending on context and implementation, an entirely new object may be returned. So for maximum safety, pass back the results of merge and use them as a replacement for the original property.


Where do you suggest I should pass back to? In the current update method I have:


I tried passing it back like this:



But this did not work. Would appreciate your suggestions on the best way to pass back the merge results.


Like so:



Note that in my earlier example, I used updateCountry() to start the detail editing operation. The saveEdits() and cancelEdits() methods end the detail editing operation.

The example above assumes that you'll be using foundRecord for further work without replacing its value with a different record to edit or add. If you don't need it, just null out foundRecord in the saveEdits() method and discard the results of countryService.updateCountry. Which, as you can probably deduce I changed to "return em.merge(newRecord); "

each defined entity implements a hash and equals method

Not sure what you mean here. Do you mean the entity defined in the domain package class? Or the way the entity is defined by the EL in the jsf pages?

This is a fundamental requirement of the JPA/EJB entity specification dating all the way back to EJB 1.0. The EJB EntityManager determines if 2 objects refer to the same row in the database based on whether or not their Entity class instances compare equal as determined by the equals() method. The hash() method is used to help the system maintain in-memory caching of the objects for better performance. And anything else where a reliable hash value would be useful.

Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

SessionScope is the lowest-common denominator and if you cannot get things to work there, then ConversationScope isn't likely to work properly either


This is likely to be the root of my problem. I'm looking into it now but was puzzles me is why a create operation would work using ConversationScoped but return blank values using SessionScoped. As the 'lowest common denominator' I'd expect SessionScoped to process any values that are processed using ConversationScoped. I'll look into this now.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

Hopefully I'm narrowing down the problem here, but I can't test if SessionScoped is what will allow records to be updated because I can't yet get new records to be created correctly using SessionScoped.

I discovered that if I use SessionScoped importing javax.enterprise.context library creating new records works fine. But the correct library should be javax.faces.bean, but that results in a blank page being rendered after the create method is called. Actually even ConversationScoped using the javax.faces.bean has the same result.

If I use SessionScoped and the faces library, the records are actually being created in the DB but not being rendered as a list when the AllCountry page is called in the create method (in the backing bean):



The resulting page (AllCountry.xhtml) is just blank. It should show a list of all the countries created so far and a 'New Country' link to create a ne country record.



If I click the New Country link, I am correctly taken to the input form BUT the value of last country created is in the input box! If I replace it with another country name, the new country name is successfuly created but I still get a blank sheet where I should see a list of countries in AllCountry.xhtml.

At this point, my best guess is to look at the methods in the DAO class:


The insert method looks quite standard and I can't think of how this would prevent the record being rendered. The findAll method however uses a normal List object where the countrylist object in the xhtmlk file is a ListDataModel object. I'm wondering if this is preventing a list of countries being rendered after each new country is created.

Failing that, would you have any idea why I'm not able to get a list of countries after creating a new country? Also why do I get the value of the last country created in the input box when i try to create a new country?

When I'm able to get this list to show correctly I can test if the SessionScoped allows my records to be updated I HOPE!
Dieter Quickfend
Bartender

Joined: Aug 06, 2010
Posts: 542
    
    4

<h:column> takes <f:param>?

I wouldn't worry about the new country thing, you are probably saving it in the session and keeping it as session state, so it's just a matter of empty the reference once you've created it.

Are you not getting any errors? Isn't your method returning an empty list?
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

Yes I'm getting an empty list as a result. Are you suggesting this is because I'm including <f: param> in the <h:column> tag?
Dieter Quickfend
Bartender

Joined: Aug 06, 2010
Posts: 542
    
    4

No no, if you're getting an empty list as a result of the getAllCountries(), we can exclude (for now) any issues on the UI side. Check if your database contains any countries, and if your entity is correctly specified. You're using an entity name, is it specified in your persistence unit?
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

The records are being corectly persisted to the database and show up correctly even though an empty list is returned.

You're using an entity name, is it specified in your persistence unit?


I'm not sure I understand the question. I have an Entity Class (Country.java) in a domains package, which specifies the entity name. Then I use this entity class in a backing bean, interface class, dao class. The dao class uses the Entity Manager to create and process the queries. Hope this answers your question. Let me know if you want me to paste any of the classes I'm using. IOtherwise I don't want to to overload this thread with superflous code. Many thanks!
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

Just to eliminate any confusion. JSF has absolutely ZERO database functionality. Any problems with the database are not due to JSF or what scope your JSF backing beans are in, just in how you are accessing the database.

Where scope becomes an issue is because JSF can create and destroy backing beans at different times, depending on what scope they have been declared in. In other words, use the wrong scope and the backing bean that you used to begin an operation won't be the same object you use in a later stage of the workflow. Which will cause state information set in the earlier stage to be lost. It can also mean the difference between starting a workflow with a "clean" bean and a bean that's burdened with lint from a previous workflow. And the presence or absence of this state info/lint is what is probably causing problems here.

Request scope, as I have often said, is almost completely useless in JSF. That is because objects in this scope exist only for the duration of a single HTTP request/response cycle, and JSF's postback approach means that a given form may involve more than one cycle before the view's form is accepted and processed. And specifically, it loses state information in the JSF DataModel objects.

Session scope means that the bean is constructed when first requested, and thereafter lives until the session is destroyed - either explicitly or by timing out. As I said earlier, you can forcibly remove an object from session scope using raw J2EE code, but JSF has no API to do that. Because of the extended life of session scope, objects in this scope will carry over information from previous uses unless you explicitly clean them (I usually have an "init()" method for that). And since the objects hang around in memory even while not being used, they can waste valuable memory space.

View scope is a special case of session scope where the backing bean is destroyed when you navigate to a different View. This gets rid of the "hanging around in memory" issue and makes it more likely that you'll start a workflow with a clean (freshly-constructed) backing bean. However, since only a single View can be tied to the backing bean, cases where you have one view to show the table and another to do detail edits causes loss of state since the bean will be destroyed when you move from one of these views to the other.

Conversation state is intended to map an actual workflow. Meaning that when you begin an operation, a fresh instance of the bean is created and it remains in the session until the conversation is ended. However, unlike the original JSF, where the bean is a passive object handled by external forces (Inversion of Control), the conversation has to be manually managed by code inside the bean. If you do not do so, what you effectively have is a Request-scope bean with all the liabilities I described above.

Regardless of scope, JSF backing beans are not "program modules" the way that servlets are. They are repositories for properties and they have invokable functions, but they don't have a functional flow of their own. In other words, they're a lot like Windows DLLs. The key to their proper use is proper management of their existence and internal state. It's actually not that hard once you have understanding. There are certain functional patterns that I use over and over and over again. One of them is the table-based CRUD process such as what we are discussing here.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

I'm all for eliminating confusion:

JSF has absolutely ZERO database functionality


Right. What I meant to say was that the DB records are being succesfully created through the JPA entities and operations, which implies that the problem is more likely to be in how the List is actually being rendered. The required values are there, but are not being rendered for some reason. I'm suspecting the problems in getting SessionScope to create the backing bean using the faces library is part of that problem. Another explanation could be the fact (as I explained earlier) that the findAll method which renders the list is using a normal List object. I don't know how to use an equivalent method that uses a ListDataModel object. Can you shed light on this?

Given your excellent explanation in your last post, SessionScoped is clearly the preferred method in which to test a CRUD application, especially in its early stages. If the backing bean can't be created using SessionScoped there is little point in trying other scopes since SessionScopes is the lowest common denominator. This is clear and understood. My question is still why SessionScoped renders values if I'm using different API libraries? This is why I'm suspecting the problem to be in how the findAll method renders the records and uses a normal List. This is why I'm asking if this could be part of the problem. Failing that, do you know of any reason why I'm unable to get SessionScoped to function given what I have said?

I really appreciate the explanations you have given.

There are certain functional patterns that I use over and over and over again. One of them is the table-based CRUD process such as what we are discussing here


I think the best way for me to really appreciate the concept of scopes and jsf is work through my CRUD application and resolve key parctical issues like the scopes and the problems in updating records. A fully functioning CRUD application will give me a great insight into the inner working of JSF. I really apprecite your time in helping me get there!
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

You need to forget about the "Faceslibrary" concept. JSF is designed to work as much as possible using POJO principles, so the more javax.faces imports your code has, the more likely you're not doing it right. Also scopes don't do anything. A scope is simply the rules that determine when objects may be added and removed from the webapp context. The actual adding and removing is done either manually (traditional J2EE) or by the JSF Managed Bean subsystem.

The one exception to the POJO rule is are the model classes, DataModel and SelectItem, which act as an intermediary between your raw data and the UI. In the case of DataModel, it decorates your underlying display data list with cursor information and adds the rowData and rowIndex methods so that you can tell which row was selected without the ugliness of having to include a parameter on the View declaration. Which pollutes the MVC paradigm by placing what is effectively code in the View Template.

The JSF scoping is based on traditional J2EE scoping, except that JSF will construct backing beans (Managed Beans) on demand automatically if they don't already exist. There's no special magic there, although the View and Conversation scopes provide a more automated way to remove J2EE Session Scope objects once you no longer need them.

A lot of people pass a raw List or array directly as a dataTable value. Or at least they think they do. In actuality, an anonymous DataModel is constructed for them. But since it's anonymous, there's no easy way to get ahold of it to extract its rowData and rowIndex decorations, so that's best used for display-only tables, not editable ones.

The way that a ListDataModel works is simple. It "wraps" a POJO list of objects so that you don't have to copy the actual data from the original List to the DataModel. You do this either by providing that list as a constructor argument or by invoking the setWrappedData() method on the ListDataModel. You can also retrieve that data using getWrappedData. The ListDataModel makes no alterations to the wrapped data. It keeps all JSF-specific funcitonality in the DataModel itself.

Furthermore, you don't have to refresh the wrapping if you alter the List or any of the rows in the list. Only if you discard the original list and replace it with a new list. Which is what you would do if you called findAll() to obtain an updated list of EJBs after a CRUD database operation. To refresh the wrapping, you can simply invoke setWrappedData, passing in the new list. I destroy (and re-create) the entire DataModel myself, but that's simply because the patterns I've evolved become simpler when I'm building from scratch instead of updating existing objects.

If you're interested in looking at a complex real-world CRUD application, you can find my "technology sandbox" app source here: https://svn.mousetech.com/svn/jtaonthemove/trunk/jtaonthemove/ I should warn you that it's not the prettiest code you'll ever find and on top of that, it's old enough that parts of it have been re-worked, but more parts need modernizing. Still, it's a fully-functional web application based on JPA and JSF.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

In the case of DataModel, it decorates your underlying display data list with cursor information and adds the rowData and rowIndex methods


So that cursor information could be what's preventing data from renedering correctly if the rowData and RowIndex methods are not appropriate for a ListDataModel object?


In actuality, an anonymous DataModel is constructed for them. But since it's anonymous, there's no easy way to get ahold of it to extract its rowData


So by creating a ListDataModel object ini my code I'm essentially removing the anonimity of the DataModel, which means I'm able to use the rowData to extract and manipulate rows?


you don't have to refresh the wrapping if you alter the List or any of the rows in the list. Only if you discard the original list and replace it with a new list. Which is what you would do if you called findAll() to obtain an updated list of EJBs after a CRUD database operation. To refresh the wrapping, you can simply invoke setWrappedData, passing in the new list. I destroy (and re-create) the entire DataModel myself, but that's simply because the patterns I've evolved become simpler when I'm building from scratch instead of updating existing objects.


I'm not sure if i do discard the original list or not after the create operation.



The AllCountry jsf page is called which calls findAll(), but does that mean it's calling the original list or discarding it and creating a new one? If it's creating a new one how do I refresh the wrapping? Do I invoke the setWrappedData method after the getRowData(0 method in the backing bean? Do I include a getWrappedData() method before the findAll()? How do you destory and re-create the model?

I'm looking at your CRUD application which will hopefully give me some answers. Would appreciate any answers or insights you may have on the questions I raised above. Thanks!




Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

Jay Tai wrote:
In the case of DataModel, it decorates your underlying display data list with cursor information and adds the rowData and rowIndex methods


So that cursor information could be what's preventing data from renedering correctly if the rowData and RowIndex methods are not appropriate for a ListDataModel object?


Well, no. What keeps data from rendering is if the datamodel either has no wrapped data or if the data (list) is empty. The rowData and rowIndex are methods that an action method can use to determine which row contained the commandButton or commandLink that fired the method. And in cases where the control that fired the action method wasn't in a row in that table, those methods won't return anything.



So by creating a ListDataModel object ini my code I'm essentially removing the anonimity of the DataModel, which means I'm able to use the rowData to extract and manipulate rows?


Not so much removing anonymity as creating a DataModel that's part of your model (backing bean). Anonymous datamodels just sort of float around in nowhere where you cannot get access to them in order to exploit their cursor-tracking abilities.


I'm not sure if i do discard the original list or not after the create operation.

The AllCountry jsf page is called which calls findAll(), but does that mean it's calling the original list or discarding it and creating a new one? If it's creating a new one how do I refresh the wrapping? Do I invoke the setWrappedData method after the getRowData(0 method in the backing bean? Do I include a getWrappedData() method before the findAll()? How do you destory and re-create the model?

I'm looking at your CRUD application which will hopefully give me some answers. Would appreciate any answers or insights you may have on the questions I raised above. Thanks!


findAll would not "call" the list, but it returns a list. Which, depending on how EJB is handling things, could simply be the same list (assuming no adds/deletes/updates), but you shouldn't depend on that. Otherwise why go to the effort to find it all over again? So if you do a "myDataModel.setWrappedData(findAll())" that would update the DataModel with the latest "findAll" results so that when the action method returns and JSF renders the page it will have the latest version of the data displayed. The previous wrapped data list will be discarded and, assuming no other references to it exist, eventually garbage-collected.


Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

findAll would not "call" the list, but it returns a list. Which, depending on how EJB is handling things, could simply be the same list (assuming no adds/deletes/updates), but you shouldn't depend on that. Otherwise why go to the effort to find it all over again?


I'm assuming the original list is discarded because the "AllCountryPage" is called in the create method.

The EJB callls an interface which in turn uses a DAO class that has the findAll() methods. The DAO class is:



So in case the page is discareded and I need to get the data back, I'm using the setWrappedData() in the backing bean as follows:




But tthis causes an error - 'java.lang.String cannot be cast to java.util.List'

I don't get any error if I include the setWrappedData in the setter method for countrylist:




But I still get a blank page.




Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

The setCountryList method doesn't have to be public. The countryList is a read-only property.

I think your class cast exception may be lack of parameterizing. Try this, instead:



If you're not seeing any row data, show me the xhtml. Because it's either missing something or the findAll() method isn't returning any rows.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

I tried that and it eliminated the class cast exception, but I stilll get a blank. The xhtml is:





Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

I also tried:



Same result. No Rows
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

your f:param elements are meaningless and should be removed.

That probably won't make data display, though. Make sure that the table is enclosed within a JSF h:form. You may also want to put some diagnostic code in your getCountryList:



"log" can be a System.out.println or whatever logger you are using.
Jay Tai
Ranch Hand

Joined: Apr 25, 2012
Posts: 162

I removed the <param> tags and the datatable is enclosed with <form> tags. I also added diagnostics as follows:



Diagnostics for the country List are:

INFO: Country List is: : javax.faces.model.ListDataModel@c178c3a
INFO: Country List is: : javax.faces.model.ListDataModel@c178c3a
INFO: Country List is: : javax.faces.model.ListDataModel@c178c3a

But nothing comes up for the ListDataModel, which suggests that makeCountryList is not even being called?
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16019
    
  20

A System.out.print on a DataModel or java.util.List doesn't show you much. To actually see what's in those objects you'd have to retrieve and print the actual elements. Which is one reason why I used the "size()" method in my example.

For you to be getting what you are getting, the countryList object must evidently have already been initialized. You should have declared it thus:


And you should have no other code that sets the countryList property.

Also, to avoid possibly stale data, if you are running the Tomcat webapp server, stop the server and delete everything in its work and temp directories. Otherwise it might recycle the obsolete session object between tests.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Problems using merge method to update DB record