问题描述
我在 上显示以 BLOB 形式存储在 MySQL 中的图像,如下所示.
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>
关联的 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();
}
}
基于唯一行标识符从数据库中检索图像的 bean - BrandBean
.
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
中的 () 方法被调用.
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 server 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).
新更新的图像仅在通过按 刷新页面时显示(在大多数浏览器上).它甚至不会在页面加载时显示(在地址栏中输入 URL,然后按 键).
The newly updated image is displayed only when the page refreshed by pressing (on most browsers). It is even not displayed on page load (entering a URL into the address bar and then pressing the key).
换句话说,当数据表中的一行通过点击指示的勾进行更新时,
getImage()
方法不是调用(因此,新图像不会从数据库中获取以显示在 上).此方法仅在通过按 快捷键刷新/重新加载页面时调用.
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 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.
推荐答案
图像正在被网络浏览器缓存.资源通过响应标头中设置的缓存相关指令基于每个 URL 进行缓存.您的具体问题是由于资源 URL 仍然相同,并且网络浏览器不知道服务器端的资源已更改而引起的.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:
将
lastModified
列添加到brand
表:
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.
这篇关于BLOB 图像仅在通过 p:dataTable 在 PrimeFaces 中进行更新后刷新页面时显示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!