BLOB图像仅在通过p更新后刷新页面时显示:dataTable在PrimeFaces中生成

Tin*_*iny 7 jsf tomcat primefaces graphicimage jsf-2.2

我在MySQL上显示以BLOB形式存储的图像<p:graphicImage>,如下所示.

<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>
Run Code Online (Sandbox Code Playgroud)

编辑行时,<p:fileUpload>会显示a <p:overlayPanel>.为简单起见,在这个例子中省略了这个和许多其他的东西,因为它们与具体问题无关.

关联的JSF托管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();
    }
}
Run Code Online (Sandbox Code Playgroud)

根据唯一行标识符从数据库中检索图像的bean - 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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

通过单击<p:rowEditor>数据表的最后一列中的定位(由...指示)更新行(在编辑之后),将调用该行中的getImage()方法BrandBean.

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

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


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

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

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


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

从表面上看,这既不应该与Spring也不应该与JPA相关(点击勾选后更新操作会正确地传播到数据库).这应该与Tomcat服务器相关.

Bal*_*usC 1

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

该图像正在被网络浏览器缓存。通过响应标头中设置的与缓存相关的指令,以每个 URL 为基础对资源进行缓存。您的具体问题是由于资源 URL 仍然相同而 Web 浏览器不知道资源在服务器端已更改而引起的。OmniFaces展示页面详细解释了缓存(注意:过滤器不是此问题的解决方案)CacheControlFilter

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

  • 向表中添加一lastModifiedbrand

    ALTER TABLE brand ADD COLUMN lastModified TIMESTAMP DEFAULT now();
    
    Run Code Online (Sandbox Code Playgroud)
  • Brand使用适当的属性扩展实体并@PreUpdate设置它:

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

    @PreUpdateJPA 在每次查询之前调用带注释的方法UPDATE

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

    <p:graphicImage value="#{brandBean.image}" ...>
        <f:param name="id" value="#{row.brandId}" />
        <f:param name="v" value="#{row.lastModified.time}" />
    </p:graphicImage>
    
    Run Code Online (Sandbox Code Playgroud)

    (为了清楚起见,我在这里将row其重命名为to以消除重复)brandbrandIdid

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

    <p:graphicImage value="#{brandBean.image}" cache="false" ...>
    
    Run Code Online (Sandbox Code Playgroud)

设计注意事项:如果图像不一定在每次更新时都更新Brand,则将其拆分到另一个表中Image,并让Brand具有 FK(@ManyToOne@OneToOneImage。这也使得属性“image”可以在 web 应用程序中的各个实体之间重复使用。