在PrimeFaces中通过p:dataTable进行更新后,仅在刷新页面时显示BLOB图像 [英] BLOB images are displayed only on refreshing a page after an update via p:dataTable is made in PrimeFaces

查看:129
本文介绍了在PrimeFaces中通过p:dataTable进行更新后,仅在刷新页面时显示BLOB图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在按<p:graphicImage>在MySQL中以BLOB形式存储图像,如下所示.

I'm displaying images stored in the form of BLOB in MySQL on a <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>

在编辑行时,在<p:overlayPanel>上显示<p:fileUpload>.在本示例中,为了简单起见,省略了这些以及许多其他内容,因为它们与具体问题无关.

While editing of a row, a <p:fileUpload> is displayed on a <p:overlayPanel>. This and many other things are omitted in this example for the sake of simplicity as they are not related to the concrete problem.

关联的JSF托管bean:

The associated JSF managed 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();
    }
}

基于唯一行标识符-BrandBean从数据库检索图像的bean.

The bean that retrieves images from the 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));
        }
    }
}

当通过单击数据表最后一列(由<p:rowEditor>指示)中的刻度来更新(编辑后)一行时,将按应调用BrandBean中的getImage()方法.

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

这在使用PrimeFaces 5.0和JSF 2.2.6在GlassFish服务器4.0上运行的应用程序中正确发生.

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

在数据表(因此在数据库中)更新一行后,新图像将立即显示在数据表中.

A new image will be displayed in the data table immediately after a row is updated in the data table (and consequently in the database).

在另一个使用Spring 4.0.0 GA的Tomcat服务器8.0.5上运行的应用程序中,在更新数据表所保存的行后,未调用getImage()方法,导致仍然在数据表中显示旧图片(不是新更新的图片)(即使更改已正确传播到数据库中).

There is another application running on Tomcat server 8.0.5 using Spring 4.0.0 GA in which the getImage() method is not invoked after a row held by the data table is updated resulting in still displaying the old image (not the newly updated one) in the data table (even though the changes are correctly propagated to the database).

仅在通过按 F5 (在大多数浏览器上)刷新页面时,才会显示新更新的图像.它甚至不会在页面加载时显示(在地址栏中输入URL,然后按 enter 键).

The newly updated image is displayed only when the page refreshed by pressing F5 (on most browsers). It is even not displayed on page load (entering a URL into the address bar and then pressing the enter key).

换句话说,当通过单击由<p:rowEditor>指示的对勾来更新数据表中的一行时,不会调用getImage()方法(因此,不会从数据库中获取新图像以在其上显示) <p:graphicImage>).仅当通过按 F5 快捷键刷新/重新加载页面时,才调用此方法.

In other words, when a row in a data table is updated by clicking the tick indicated by <p:rowEditor>, the getImage() method is not invoked (hence, the new image is not fetched from the database to be displayed on <p:graphicImage>). This method is invoked only when the page is refreshed/reloaded by pressing the F5 shortcut key.

为什么会这样?如何在更新行后立即显示新更新的图像?

Why does this happen? How to show a newly updated image immediately after a row is updated?

从表面上看,这既不应与Spring无关,也不应与JPA无关(单击勾号后,更新操作将正确传播到数据库).这应该与Tomcat服务器有关.

Superficially, this should neither be related to Spring nor JPA (the update operation is correctly propagated to the database after clicking the tick). This should rather be related to Tomcat server.

推荐答案

仅在通过按F5键刷新页面时才显示新更新的图像(在大多数浏览器上).它甚至不会在页面加载时显示(在地址栏中输入URL,然后按Enter键).

网络浏览器正在缓存图像.资源通过响应标头中设置的与缓存相关的指令按URL进行缓存.造成您的具体问题的原因是,资源URL仍然相同,并且Web浏览器不知道服务器端的资源已更改. OmniFaces CacheControlFilter展示页面详细说明了缓存(注意:过滤器不是解决方案这个问题).

The image is being cached by the webbrowser. Resources are cached on a per-URL basis via the cache-related instructions set in the response headers. Your concrete problem is caused because the resource URL is still the same and the webbrowser isn't aware that the resource has changed in the server side. The OmniFaces CacheControlFilter showcase page explains caching in detail (note: the filter is not the solution to this problem).

您基本上需要通过更改URL来强制Web浏览器重新请求资源.在这种情况下,可缓存资源突然更改,并且其更改需要立即反映给所有客户端的一种最常见的方法是,将图像的上次修改时间"时间戳附加到URL的查询字符串中.鉴于您正在使用JPA,因此应该这样做:

You basically need to force the webbrowser to re-request the resource by changing the URL. One of most common approaches for this kind of situation, whereby a cacheable resource is suddenly changed and its change needs to be immediately reflected to all clients, is appending the "last modified" timestamp of the image to the query string of the URL. Given that you're using JPA, so this should do:

  • brand表中添加lastModified列:

ALTER TABLE brand ADD COLUMN lastModified TIMESTAMP DEFAULT now();

  • 使用适当的属性和

  • Extend the Brand entity with the appropriate property and a @PreUpdate which sets it:

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

    (在每次UPDATE查询之前,JPA都会调用一个@PreUpdate带注释的方法)

    (a @PreUpdate annotated method is invoked by JPA right before every UPDATE query)

    将其附加到图像URL(参数名称v是对版本"的提示):

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

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

    (为清楚起见,我将row重命名为brand,将brandId重命名为id进行重复数据删除)

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

    最后,如果您使用的是PrimeFaces 5.0或更高版本,则还需要禁用服务器端缓存:

    Finally, if you're using PrimeFaces 5.0 or newer, then you also need to disable server side caching:

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

  • 设计注意事项:如果图像不一定在每次Brand更新时都进行更新,则将其拆分到另一个表Image中,并让Brand将FK(@ManyToOne@OneToOne)设置为Image.这也使属性图像"可在Web应用程序中的各个实体之间重复使用.

    Design notice: if the image is not necessarily updated on every update of Brand, then split it off to another table Image and let Brand have a FK (@ManyToOne or @OneToOne) to Image. This also makes the property "image" reusable across various entities in the webapp.

    这篇关于在PrimeFaces中通过p:dataTable进行更新后,仅在刷新页面时显示BLOB图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆