2012-05-04 5 views
0

У меня есть компонент @ViewScoped, который вызывает bean-компонент @Stateless, который выполняет простой запрос, чтобы возвращать некоторые значения из моей БД.Вызов SQL Query возвращает старое значение

Этого должно быть достаточно, чтобы делать запрос каждый раз, когда я загружаю страницу, и это должно привести к тому, что всегда будут обновляться данные при каждой загрузке страницы.

Но это не сработает, и я не знаю, как его решить!

Мой запрос возвращает старое значение, даже после его изменения с помощью MySql Workbench. (Выполнение запроса на 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 + " ]"; 
    } 

} 

Теперь, я столкнулся с этой проблемой, прежде чем с другими структурами и методами, и я решил ее обновив сущности, но почему я должен обновить сущность в этом случае, если я его из базы данных каждый раз, когда я загружаю страницу?

Это просто вздор!

+1

Вы установили обновление в Workbench MySQL? –

+0

Конечно, да. На самом деле у меня есть правильный результат, если я вручную сделаю запрос в Workbench, и у меня их есть на моей странице, только если я повторно разворачиваю приложение: \ – StepTNT

+0

Это означает, что сеанс имеет слишком долгий срок службы. Когда он открывается и закрывается? –

ответ

3

Из самого кода это не похоже, что вы делаете явное кэширование самостоятельно. @ViewScoped, @RequestScoped и isPostback здесь не актуальны, и, наоборот, цель этих областей заключается в том, чтобы на самом деле делать кеширование, а не позволять бэксовому абоненту обращаться к службе каждый раз.

Это, однако, почти противоположно вашей проблеме.

В случае, если вы получаете устаревшие объекты из диспетчера объектов, это почти всегда случай кэша L2. Вы настроили какой-либо файл persistence.xml? Какую реализацию JPA вы используете?

Также важно, где и как вы обновляете свои данные? Приведенный код не показывает его. Вы упомянули об этом «даже после смены его с помощью MySql Workbench»

В случае использования кеша JPA уровня 2 (L2) JPA получит объекты из этого кеша. Без встречных мер он будет отслеживать изменения этих объектов только в том случае, если они будут изменены через JPA. Если вы обновляете базовые данные самостоятельно, либо напрямую через JDBC, либо через какую-либо другую внешнюю систему (например, MySql Workbench), JPA не будет знать об этих изменениях.

+0

Я использую 'EclipseLink'.Фактически, переход от ' ALL' to ' NONE' решил мою проблему, но я боюсь что это может повлиять на мои сеансовые компоненты. Я могу установить его в ' ENABLE_SELECTIVE', но я не знаю, что это на самом деле делает! – StepTNT

+1

Обычно вы должны указать для каждого объекта, хотите ли вы его кешировать или нет. Настройки кэширования JPA, как правило, не влияют на сеансовые компоненты. Если вы обновляете данные непосредственно в своей БД, а не через диспетчер сущностей или если несколько компьютеров разговаривают с одной и той же БД, которые не знают друг о друге, вам не следует использовать кэш L2 для этих объектов. –

-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 
} 
+0

Я проверю его, как только у меня есть ноутбук с проектом. Можете ли вы объяснить, почему мне нужно знать, является ли моя страница обратной почтой и что я должен делать в этом случае? Спасибо :) – StepTNT

+0

Если вы используете JSF, в жизненном цикле JSF есть шесть фаз Исходный запрос проходит только через: Восстановить View & Render Response, нажав ссылку URL Post Back сделан на всех этапах, а именно: Восстановить представление, Применить значение запроса, Проверка процесса, Обновить значения модели, Вызовите приложение и Обработать ответ, нажав кнопку «Отправить». См. Здесь для получения дополнительной информации: http://download.oracle.com/javaee/1.4/tutorial/doc/JSFIntro10.html –

+0

Подождите! Проблема возникает перед просмотром данных на моей странице JSF. На странице отображаются неверные данные, поскольку бэк-файл возвращает неверные данные. Но проблема в том, что бэк-компонент выполняет запрос каждый раз, когда вызывается getter, поэтому я не могу понять, почему запрос возвращает старые значения! – StepTNT

0

Вы пытались изменить свой боб в @RequestScoped?

+0

Я пробовал просматривать и запрашивать, без успеха. – StepTNT

+0

Сфера не имеет ничего общего с проблемой, так как код вызывает службу каждый раз и не хранит ничего в скобках. –

1

Мой инстинкт заключается в том, что у вас есть устаревший кеш.

Ты прочитал (а) this article?

Сначала я бы сосредоточил внимание на вашем сеансе. Создайте тестовый жгут без дополнительной сложности страниц JSF.

Я ожидал, что поведение транзакций по умолчанию вашего Stateless боба быть «разумным», но я теперь интересно ли с помощью

@TransactionAttribute(TransactionAttributeType.REQUIRED) 

может решить вашу проблему.

+0

По умолчанию EJB являются разумными, а «обязательным» является значение по умолчанию;) В любом случае, независимо от того, выполняется ли запрос выбора в транзакции или нет, здесь нет никакой разницы. –

+0

Согласитесь, нет очевидной причины, по которой нам нужно будет запрашивать Required, но мы не знаем всей истории. Что касается транзакций, возможно, я не понимаю статью, которую я упоминаю, но, похоже, некоторые транзакционные эффекты на срок службы кеша. Поэтому первым моим шагом было бы сделать вдвойне уверенность в том, что я контролирую продолжительность транзакции, а затем перейду в настройки JPA. – djna

1

Скорее всего, это вызвано уровнем изоляции MySQL по умолчанию, который является ПОВТОРНЫМ ПРОЧИТАЕМ.

Это означает, что вы не видите изменения, сделанные другими транзакциями, пока не закончится (совершить, откат) свой «собственный» сделки (помните: ОТБОРНОЕ уже начинает транзакцию)

Я предполагаю, что соединение EJB является взятые из пула соединений, и, таким образом, запущенные транзакции никогда не заканчиваются должным образом. Попробуйте выполнить фиксацию или откат до запуска выбора из вашего веб-приложения.

Для постоянного решения вы можете либо изменить изоляцию по умолчанию, настроив пул подключений (большинство из них разрешить это), изменив уровень транзакции, вызвав setTransactionIsolation() в соединении, либо изменив уровень изоляции по умолчанию в MySQL.

+1

> Попробуйте выполнить фиксацию или откат перед запуском select - в компоненте EJB, который вы обычно не делали бы этого. EJB заботится об управлении транзакцией. Поскольку вызов начинается с не-TX-контекста (бэк-компонента), запрос выбора полностью запускается в своем собственном TX. –

+0

@ArjanTijms: да, я это понимаю, но проанализирую реальную проблему, которая бы быстро прошла тест. Я думаю, что изменение уровня изоляции по умолчанию в пуле соединений будет лучшим решением (если уровень изоляции действительно является основной причиной) –

+1

Это зависит от того, как OP обновляет свои данные. Если это также сделано в EJB без BMT (по умолчанию), тогда нет понятия, что нужно что-либо совершить. EJB автоматически выполнит или откат, если метод ввода завершится. Поскольку OP имеет ту же проблему при обновлении базы данных с помощью Workbench AND Workbench, она видит обновленные данные, я думаю, что маловероятно, что OP обновляет данные в первую очередь в EJB, который еще не совершил (в общем, это действительно возможность). –

1

Вы используете спящий режим в качестве своего EntityManager? Если это так, это может быть использование кеша сеанса и сохранение вашего объекта. В этом случае, если вы измените данные либо через SQL, либо через другой сеанс, вам может потребоваться вызвать «обновление» на вашем объекте, чтобы получить изменения.

+0

Кэш сеанса - это l1, который не может быть проблемой, поскольку обновление, предположительно, можно наблюдать с помощью внешних инструментов. Транзакция для запроса выбора начинается после того, как данные были обновлены, и после этого начинается новый контекст (сеанс) сохранения, только один раз запрашивает данные, а затем завершает контекст. Это не может быть проблемой L1/Session. –