0

DB からいくつかの値を返す単純なクエリを実行する Bean を呼び出す Bean があります@ViewScoped@Stateless

これは、ページをロードするたびにクエリを作成するのに十分なはずであり、これにより、ページのロードごとに常にデータが更新されるようになります。

しかし、これは機能しません。解決方法がわかりません。

MySql Workbench で変更した後でも、クエリは古い値を返します。(ワークベンチでクエリを実行すると、正しいデータが返されます!)

コードは次のとおりです。

DispensaListBean.java

package ManagedBeans;

import ejb.DispensaManager;
import ejb.DispensaManagerLocal;
import entity.Dispensa;
import java.util.List;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

/**
 *
 * @author stefano
 */
@ManagedBean
@ViewScoped
public class DispensaListBean {
    @EJB
    private DispensaManagerLocal dispensaManager;


    /**
     * Creates a new instance of DIspensaListBean
     */
    public DispensaListBean() {
    }

    public List<Dispensa> getTopDispense(){
        List<Dispensa> l = dispensaManager.findByVoto(DispensaManager.DESC);
        for(Dispensa d : l){
            System.out.println(d.getTitolo() + " | " + d.getVoto()); //This code prints ALWAY the old getVoto() value, it takes the new one just after restarting the server
        }
        return l;
    }

    public List<Dispensa> getDispense(){
        return dispensaManager.findAll();
    }

    public Dispensa getById(int i){
        return dispensaManager.findById(i);
    }
}

DispensaManager.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ejb;

import entity.Dispensa;
import facade.DispensaFacadeLocal;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;

/**
 *
 * @author stefano
 */
@Stateless
public class DispensaManager implements DispensaManagerLocal {

    public static final int ASC=0, DESC=1;

    @EJB
    private DispensaFacadeLocal dispensaFacade;

    @Override
    public java.util.List<Dispensa> findByVoto(int order) {
        return (order==DispensaManager.ASC) ? dispensaFacade.findByVotoAsc() : dispensaFacade.findByVotoDesc();
    }

    @Override
    public List findAll() {
        return dispensaFacade.findAll();
    }

    @Override
    public Dispensa findById(int id) {
        return dispensaFacade.find(id);
    }                
}

DispensaFacade.java

package facade;

import entity.Dispensa;
import entity.Post;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

/**
 *
 * @author stefano
 */
@Stateless
public class DispensaFacade extends AbstractFacade<Dispensa> implements DispensaFacadeLocal {
    @PersistenceContext(unitName = "UNILIFE-ejbPU")
    private EntityManager em;

    @Override
    protected EntityManager getEntityManager() {
        return em;
    }

    public DispensaFacade() {
        super(Dispensa.class);
    }

    @Override
    public List<Dispensa> findByVotoDesc() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Dispensa> q = cb.createQuery(Dispensa.class);
        Root<Dispensa> c = q.from(Dispensa.class);
        q.select(c);
        q.where(cb.isNotNull(c.get("datiFile")));
        q.orderBy(cb.desc(c.get("voto")));
        TypedQuery<Dispensa> typedQuery = em.createQuery(q);
        return typedQuery.getResultList();
    }

    @Override
    public java.util.List<Dispensa> findByVotoAsc() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Dispensa> q = cb.createQuery(Dispensa.class);
        Root<Dispensa> c = q.from(Dispensa.class);
        q.select(c);
        q.where(cb.isNotNull(c.get("datiFile")));
        q.orderBy(cb.asc(c.get("voto")));
        TypedQuery<Dispensa> typedQuery = em.createQuery(q);
        return typedQuery.getResultList();
    }
}

Dispensa.java

package entity;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

/**
 *
 * @author stefano
 */
@Entity
@Table(name = "Dispensa")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Dispensa.findAll", query = "SELECT d FROM Dispensa d"),
    @NamedQuery(name = "Dispensa.findById", query = "SELECT d FROM Dispensa d WHERE d.id = :id"),
    @NamedQuery(name = "Dispensa.findByTitolo", query = "SELECT d FROM Dispensa d WHERE d.titolo = :titolo"),
    @NamedQuery(name = "Dispensa.findByDescrizione", query = "SELECT d FROM Dispensa d WHERE d.descrizione = :descrizione"),
    @NamedQuery(name = "Dispensa.findByTag", query = "SELECT d FROM Dispensa d WHERE d.tag = :tag"),
    @NamedQuery(name = "Dispensa.findByData", query = "SELECT d FROM Dispensa d WHERE d.data = :data"),
    @NamedQuery(name = "Dispensa.findByVoto", query = "SELECT d FROM Dispensa d WHERE d.voto = :voto"),
    @NamedQuery(name = "Dispensa.findByNumVoti", query = "SELECT d FROM Dispensa d WHERE d.numVoti = :numVoti"),
    @NamedQuery(name = "Dispensa.findByNumDownloads", query = "SELECT d FROM Dispensa d WHERE d.numDownloads = :numDownloads")})
public class Dispensa implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @NotNull
    @Column(name = "id")
    private Integer id;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 50)
    @Column(name = "titolo")
    private String titolo;
    @Size(max = 255)
    @Column(name = "descrizione")
    private String descrizione;
    @Size(max = 255)
    @Column(name = "tag")
    private String tag;
    @Basic(optional = true)
    @NotNull
    @Lob
    @Column(name = "datiFile")
    private byte[] datiFile;
    @Basic(optional = false)
    @NotNull
    @Column(name = "data")
    @Temporal(TemporalType.DATE)
    private Date data;
    @Basic(optional = false)
    @NotNull
    @Column(name = "voto")
    private int voto;
    @Basic(optional = false)
    @NotNull
    @Column(name = "numVoti")
    private int numVoti;
    @Basic(optional = false)
    @NotNull
    @Column(name = "numDownloads")
    private int numDownloads;
    @JoinTable(name = "Scaricati", joinColumns = {
        @JoinColumn(name = "dispensa", referencedColumnName = "id")}, inverseJoinColumns = {
        @JoinColumn(name = "utente", referencedColumnName = "username")})
    @ManyToMany(fetch = FetchType.LAZY)
    private Collection<Utente> downloaders;
    @JoinColumn(name = "materia", referencedColumnName = "id")
    @ManyToOne(optional = true)
    private Materia materia;
    @JoinColumn(name = "autore", referencedColumnName = "username")
    @ManyToOne(optional = false)
    private Utente autore;

    public Dispensa() {
    }

    public Dispensa(Integer id) {
        this.id = id;
    }

    public Dispensa(Integer id, String titolo, byte[] datiFile, Date data, int voto, int numVoti, int numDownloads) {
        this.id = id;
        this.titolo = titolo;
        this.datiFile = datiFile;
        this.data = data;
        this.voto = voto;
        this.numVoti = numVoti;
        this.numDownloads = numDownloads;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitolo() {
        return titolo;
    }

    public void setTitolo(String titolo) {
        this.titolo = titolo;
    }

    public String getDescrizione() {
        return descrizione;
    }

    public void setDescrizione(String descrizione) {
        this.descrizione = descrizione;
    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public byte[] getDatiFile() {
        return datiFile;
    }

    public void setDatiFile(byte[] datiFile) {
        this.datiFile = datiFile;
    }

    public Date getData() {
        return data;
    }

    public void setData(Date data) {
        this.data = data;
    }

    public int getVoto() {
        return voto;
    }

    public void setVoto(int voto) {
        this.voto = voto;
    }

    public int getNumVoti() {
        return numVoti;
    }

    public void setNumVoti(int numVoti) {
        this.numVoti = numVoti;
    }

    public int getNumDownloads() {
        return numDownloads;
    }

    public void setNumDownloads(int numDownloads) {
        this.numDownloads = numDownloads;
    }

    @XmlTransient
    public Collection<Utente> getDownloaders() {
        return downloaders;
    }

    public void setDownloaders(Collection<Utente> utenteCollection) {
        this.downloaders = utenteCollection;
    }

    public Materia getMateria() {
        return materia;
    }

    public void setMateria(Materia materia) {
        this.materia = materia;
    }

    public Utente getAutore() {
        return autore;
    }

    public void setAutore(Utente autore) {
        this.autore = autore;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Dispensa)) {
            return false;
        }
        Dispensa other = (Dispensa) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "entity.Dispensa[ id=" + id + " ]";
    }

}

さて、以前に他のエンティティとメソッドでこの問題に直面したことがあり、エンティティを更新することで解決しましたが、この場合、ページを読み込むたびにデータベースからエンティティを取得する場合、エンティティを更新する必要があるのはなぜですか?

それはただのナンセンスです!

4

6 に答える 6

3

コード自体からは、明示的なキャッシュを自分で行っているようには見えません。@ViewScoped@RequestScopedおよびisPostbackこれらはすべてここでは関係ありません。逆に、これらのスコープの目的は実際にはキャッシングを行うことであり、バッキング Bean が毎回サービスを呼び出すのではありません。

ただし、それはあなたの問題のほぼ反対です。

エンティティ マネージャから古いエンティティを取得する場合、ほとんどの場合、L2 キャッシュが原因です。persistence.xml で設定しましたか? どのJPA実装を使用していますか?

また、データをどこでどのように更新するかということも重要です。与えられたコードはそれを示していません。「MySql Workbenchで変更した後でも」これについて言及していますか

JPA レベル 2 (L2) キャッシュが使用されている場合、JPA はこのキャッシュからエンティティーを取得します。対策がなければ、JPA を介して変更された場合にのみ、それらのエンティティへの変更を追跡します。JDBC を介して直接、または他の外部システム (MySql Workbench など) を介して基になるデータを自分で更新すると、JPA はそれらの変更を認識しません。

于 2012-05-06T20:14:29.733 に答える
1

私の本能は、ある種の古いキャッシュがあるということです。

この記事を読んだことがありますか?

まず、SessionBeanに焦点を当てます。JSFページの余分な複雑さなしにテストハーネスを作成します。

ステートレスBeanのデフォルトのトランザクション動作が「賢明」であると期待していましたが、使用するかどうか疑問に思っています。

@TransactionAttribute(TransactionAttributeType.REQUIRED)

あなたの問題を解決するかもしれません。

于 2012-05-06T20:16:53.473 に答える
1

ほとんどの場合、これはMySQLのデフォルトの分離レベルであるREPEATABLEREADが原因です。

これは、「自分の」トランザクションを終了(コミット、ロールバック)するまで、他のトランザクションによって行われた変更が表示されないことを意味します(覚えておいてください:SELECTはすでにトランザクションを開始しています)

EJB接続は接続プールから取得されるため、開始されたトランザクションが適切に終了することはないと思います。Webアプリケーション内からselectを実行する前に、コミットまたはロールバックを発行してみてください。

永続的なソリューションの場合、接続プールを構成してデフォルトの分離レベルを変更するか(ほとんどの場合これを許可します)、接続でsetTransactionIsolation()を呼び出すか、MySQLのデフォルトの分離レベルを変更してトランザクションレベルを変更できます。

于 2012-05-06T20:18:04.793 に答える
1

EntityManager として hibernate を使用していますか? その場合、セッション キャッシュを使用してオブジェクトを保存している可能性があります。その場合、SQL または別のセッションを介してデータを変更すると、変更を取得するためにオブジェクトで「更新」を呼び出す必要がある場合があります。

于 2012-05-06T20:43:15.303 に答える
0

Bean を に変更しようとしました@RequestScopedか?

于 2012-05-05T11:47:21.090 に答える
-1

ページがポストバックかどうかを知る必要があります。http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/javax/faces/render/ResponseStateManager.html

そんな感じ

ResponseStateManager rsm = FacesContext.getCurrentInstance().getRenderKit().getResponseStateManager();  

if (!rsm.isPostback(FacesContext.getCurrentInstance())) {  

    //do some stuff  
}  
于 2012-05-04T16:02:09.333 に答える