BLOB images are displayed only when the page is refreshed after updating through p: dataTable is executed in PrimeFaces

I display images stored in BLOB form in MySQL on <p:graphicImage> as follows.

 <p:dataTable var="row" value="#{testManagedBean}" lazy="true" editable="true" rows="10"> <p:column headerText="id"> <h:outputText value="#{row.brandId}"/> </p:column> <p:column headerText="Image"> <p:cellEditor> <f:facet name="output"> <p:graphicImage value="#{brandBean.image}" height="100" width="100"> <f:param name="id" value="#{row.brandId}"/> </p:graphicImage> </f:facet> <f:facet name="input"> <p:graphicImage id="image" value="#{brandBean.image}" height="100" width="100"> <f:param name="id" value="#{row.brandId}"/> </p:graphicImage> </f:facet> </p:cellEditor> </p:column> <p:column headerText="Edit" width="50"> <p:rowEditor/> </p:column> </p:dataTable> 

When editing a line on <p:overlayPanel> , the symbol <p:fileUpload> . This and many other things are omitted in this example for simplicity, since they are not related to a specific problem.

JSF related bean:

 @ManagedBean @ViewScoped public final class TestManagedBean extends LazyDataModel<Brand> implements Serializable { @EJB private final TestBeanLocal service=null; private static final long serialVersionUID = 1L; @Override public List<Brand> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) { setRowCount(3); return service.getList(); } } 

a bean that retrieves images from a database based on a unique row identifier - BrandBean .

 @ManagedBean @ApplicationScoped public final class BrandBean { @EJB private final BrandBeanLocal service=null; public BrandBean() {} public StreamedContent getImage() throws IOException { FacesContext context = FacesContext.getCurrentInstance(); if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) { return new DefaultStreamedContent(); } else { String id = context.getExternalContext().getRequestParameterMap().get("id"); System.out.println("id = "+id); byte[] bytes = service.findImageById(Long.parseLong(id)); return bytes==null? new DefaultStreamedContent(new ByteArrayInputStream(new byte[0])):new DefaultStreamedContent(new ByteArrayInputStream(bytes)); } } } 

When a row is updated (after editing it) by clicking on the <p:rowEditor> located (indicated by <p:rowEditor> ) in the last column of the data table, the getImage() method in BrandBean is called as it should.

This happens correctly in an application running on a GlassFish 4.0 server using PrimeFaces 5.0 and JSF 2.2.6.

The new image will be displayed in the data table immediately after updating the row in the data table (and therefore in the database).


There is another application on the Tomcat 8.0.5 server that uses Spring 4.0.0 GA, in which the getImage() method getImage() not called after updating the row stored in the data table, resulting in still displaying the old image (and not the updated one) in the data table ( even if the changes are correctly transferred to the database).

The updated image is displayed only when you refresh the page by pressing F5 (in most browsers). It does not even appear when the page loads (entering the URL into the address bar, and then pressing the enter key).

In other words, when a row in the data table is updated by clicking on the tick specified by <p:rowEditor> , the getImage() method getImage() not called (therefore, the new image is not retrieved from the database displayed on <p:graphicImage> ). This method is called only when the page is refreshed / reloaded by pressing the F5 key.


Why is this happening? How to show a recently updated image immediately after updating a row?

Superficially, this should not be connected with Spring and JPA (the update operation correctly spreads to the database after clicking on the checkmark). Rather, it should be related to the Tomcat server.

+6
source share
2 answers

The updated image is displayed only when the page is refreshed by pressing F5 (in most browsers). It does not even appear when the page loads (entering the URL into the address bar, and then pressing the enter key).

The image is cached using a web browser. Resources are cached for each URL using the cache-related instructions in the response headers. Your specific problem is because the URL of the resource is the same, and the web browser does not know that the resource has changed on the server side. The OmniFaces CacheControlFilter page shows a detailed description of caching (note: the filter is not a solution to this problem).

Basically, you need to make the web browser re-request the resource by changing the URL. One of the most common approaches to such situations, as a result of which a rapidly changing cache resource changes, and its change should immediately be reflected in all clients, adds the timestamp of the "last change" of the image to the request line of the URL. Given that you are using JPA, so this should do:

  • Add the lastModified column to the brand table:

     ALTER TABLE brand ADD COLUMN lastModified TIMESTAMP DEFAULT now(); 
  • Extend the brand object with the corresponding property and @PreUpdate , which sets it:

     @Column @Temporal(TemporalType.TIMESTAMP) private Date lastModified; @PreUpdate public void onUpdate() { lastModified = new Date(); } // +getter+setter 

    (a @PreUpdate annotated method is called by the JPA immediately before each UPDATE request)

  • Add it to the image URL (the parameter name v is a hint to the "version"):

     <p:graphicImage value="#{brandBean.image}" ...> <f:param name="id" value="#{row.brandId}" /> <f:param name="v" value="#{row.lastModified.time}" /> </p:graphicImage> 

    (I would here rename row to brand for clarity and brandId to id for deduplication)

  • Finally, if you are using PrimeFaces 5.0 or later, you also need to disable server-side caching:

     <p:graphicImage value="#{brandBean.image}" cache="false" ...> 

Design note: if the image is not necessarily updated every time the brand updated, then split it into another Image table and let the brand have FK ( @ManyToOne or @OneToOne ) to Image . It also makes the image property reusable for different objects in webapp.

+1
source

oh

I have the same problem, but my src image is in hardisk, not in DB. I have a problem when I use a random number, but I do not read it. in general, I add a parameter to the src image if this parameter is a random number.

My solution is this:

 public class UserPage { private String fotoCVPath; //all variabile private String getRandomImageName() { int i = (int) (Math.random() * 10000000); return String.valueOf(i); } public void editImage() { //init the fotoCVPath fotoCVPath = fotoCVPath + "?tmpVal=" + getRandomImageName(); } /** * @return the fotoCVPath <br> * * @author - asghaier <br> * * Created on 29/mag/2014 */ public String getFotoCVPath() { return fotoCVPath; } /** * @param fotoCVPath the fotoCVPath to set <br> * * @author- asghaier <br> * * Created on 29/mag/2014 */ public void setFotoCVPath(String fotoCVPath) { this.fotoCVPath = fotoCVPath; } } 

and on my page I have a graphicImage element like this:

 <h:panelGrid ...al param > <h:column> <p:graphicImage id="imgCV" value="#{userPage.fotoCVPath}" styleClass="imgCVStyle" /> </h:column> </h:panelGrid> 

and when I like to reload the image, refresh the grid component panel.

for my example i use

 <p:ajax event="click" onstart="PF('uploadImgCV').show();" update ="idPanelGrid"/> 

in

Hope this mode helps you solve your problem.

0
source

Source: https://habr.com/ru/post/970066/


All Articles