Tem como habilitar cache para um relacionamento one-to-one com chave estrangeira?
Cache Hibernate
48 Respostas
Dá pra botar lazy true nesse tipo de relacionamento?
?
da para habilitar lazy sim! apesar do default de relacao com ponta One ser eager.
sobre o second level cache, tasca um @Cache nessa relacao, ta resolvido.
Olá Paulo,
Quanto ao Lazy, não fuciona, ao menos quando eu uso HQL… com critéria acredito que eu consiga ter um controle maior…
Já a questão do cache… realmente não está funcionando. E já li algumas referências na net (google) que dizem que isso não é possível (mas acho que deveria ser). Tem alguma sugestão? :roll:
Será que eu estou marcando bobeira em alguma coisa…
Grato
Paulo, eu de novo…
Se eu tento colocar um cache pra relação one-to-one eu recebo a seguinte excessão:
... invalid mapping
...
SaxParseException: O conteúdo do tipo de elemento "one-to-one" deve corresponder a "(meta*|formula*)"
O que leva a crer que não é possível usar cache pra esse tipo de relação.
No lazy só são possíveis os valores: false, proxy e no-proxy.
Oi Alex
Nao sei como hbm, mas com anotacoes um simples @OneToOne(fetch=FetchType.LAZY) funciona. Entao deve ter sim como fazer no hbm. Que hibernate voce ta usando? Voce nao ta fazendo “fetch” no HQL neh?
Bem, eu nunca fiz cache de uma relacao que a ponta era One do outro lado, mas nao imagino um motivo pelo qual nao devesse funcionar. Manda os links que voce achou pra gente
Rapaz, se dá pra defirnir ela como “lazy=true” ele vai ser carregado do cache da classe assim que alguém tentar acessar a propriedade pela primeira vez, você não precisa habilitar o cache para o relacionamento.
Provavelmente não deve ser possível fazer cache nesse tipo de relação exatamente por isso, ele vai sempre pegar no cache da classe já que tá fazendo o lazy-load de qualquer jeito.
Mauricio, nao eh bem assim
Cache de relacionamentos funciona como o cache de Queries: ele guarda os IDs. Entao se o livro 7 tem o autor 5 como relacionamento, com cache de relacionamento e lazu esse numero 5 ficaria anotado em algum canto. A dynamic proxy pendurada no getAutor() ja devolve um proxy para levantar o Autor de id 5, sem ter de consultar o banco para ver quem esta relacionado com esse Livro. Pelo menos eh assim para o many. Realmente nao me parece muito util para o One na outra ponta.
O meu problema é o seguinte: eu tenho uma tabela A e uma tabela B que possuem um relacionamento one-to-one de um lado e may-to-one do outro, pois tenho um relacionamento com chave estrangeira.
A questão é que se eu chamo a tabela A (“from A where id = 1”), a tabela B vem junto… eu queria evitar esse comportamento, pois o modelo é complexo, NÃO pode ser mudado e essa característica vai prejudicar o desempenho… eu realmente não preciso da tabela B (lazy=true) na maioria dos casos e em outros casos os dados da tabela B poderiam muito bem estar em cache.
Não posso usar anotações, a solução tem que ser no hbm mesmo.
Paulo, não consegui achar o link que eu tinha lido antes… 
Não faz muito sentido você fazer cache de algo que va mudar constantemente, é legal habilitar cache de segundo nível quando você possui uma coleção de objetos que muito raramente ira ser alterada, assim o ganho de performance sera perceptivel.
Estou com um problema de Cache!
Bom, minha aplicação está rodando localmente e em outro servidor, ambos usando do mesmo banco de dados, entretanto, se eu altero um dado local, quando vou receber ele remoto, parece não estar editado, mas se eu fizer um restart do server, ele lê corretamente, portanto, gostaria de saber como desativar o cache do hibernate para algumas classes.
Valew!
Bom, se o problema for com um objeto, você pode fazer assim:
objeto = this.session.refresh(objeto);
[]'s
Daniel
isso?
@SuppressWarnings("unchecked")
public List<Filial> listar() {
this.session.refresh(Filial.class);
return this.session.createCriteria(Filial.class).list();
}
você chegou a configurar cache de 2o nível?
você diz
<property name="cache.use_second_level_cache">rrue</property>
não utilizei nada disso!
então a menos que você esteja segurando a instância da entidade durante várias requisições, e deixando a session/entityManager aberto, não é cache do hibernate.
se você está segurando a instância com a session fechada, basta fazer o session.refresh como o yorgan falou
Dessa maneira?
@SuppressWarnings("unchecked")
public List<Filial> listar() {
this.session.refresh(Filial.class);
return this.session.createCriteria(Filial.class).list();
}
não…
vc chama esse método listar toda hora? ou vc guarda o resultado dele por bastante tempo?
Não chamo toda hora não, mas, o que acontece é… se eu alterar algo, o outro local deve saber se foi modificado ou não, sem precisar de um “refresh”… pois não estaremos acessando apenas de um lugar!
toda vez que esse método for chamado, vai ser retornada a última versão do banco de dados, mesmo sem o refresh
Como resolver isso?
faça o seguinte:
- mude uma filial em um sistema
- liste as filiais no outro sistema (usando esse método!)
e vice-versa.
Isso funciona?
outra coisa: qdo vc muda as filiais em um sistema, vc faz isso dentro de uma transação? ou seja, está indo de verdade pro banco?
public void edit(Filial filial) {
Transaction tx = session.beginTransaction();
session.update(filial);
tx.commit();
}
@SuppressWarnings("unchecked")
public List<Filial> listar() {
return this.session.createCriteria(Filial.class).list();
}
Bom, está ai, updating de uma filial... Só não entendi ainda como fazer a parte do refresh! kkkkk
LEIGO! kkk
esquece o refresh, não precisa dele.
se as duas aplicação estão usando o mesmo banco mesmo, deveria funcionar
Bom fiz o teste… Adicionei localmente dois registros.
Fui no MySQL e dei select direto na base, estão inseridos.
Mas no servidor remoto não consta os dois registros…
Adicionei um novo registro no servidor remoto… então ele leu os 2 registros.
Portanto, está em cache, e preciso fazer com que as informações fiquem sincronizadas…
Pensei em Flush
session.flush() o que acha?
não é cache…
pode ser cache da página, não?
o servidor remoto está com a mesma aplicação?
exatamente a mesma aplicação, não é cash pois eu entrei na página pela primeira vez… e não reiniciei o tomcat! ou seja, continua com as mesmas informações, entretanto quando ele faz o commit, então ele carrega os dados.
bom, se vc não configurou o cache de segundo nível, a única explicação é você estar com uma mesma session aberta pra várias requisições, é isso?
Claro, isso pode ser sim… Bom fiz o seguinte:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import br.com.caelum.vraptor.ioc.ApplicationScoped;
import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.ioc.ComponentFactory;
@Component
@ApplicationScoped
public class CreateSessionFactory implements ComponentFactory<SessionFactory> {
private SessionFactory factory;
@PostConstruct
public void abre() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
this.factory = configuration.buildSessionFactory();
}
public SessionFactory getInstance() {
return factory;
}
@PreDestroy
public void fecha() {
this.factory.close();
}
}
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.ioc.ComponentFactory;
@Component
public class CreateSession implements ComponentFactory<Session> {
private final SessionFactory factory;
private Session session;
public CreateSession(SessionFactory factory) {
this.factory = factory;
}
@PostConstruct
public void abre() {
this.session = factory.openSession();
//this.session.setFlushMode(FlushMode.COMMIT);
//this.session.setCacheMode(CacheMode.REFRESH);
}
public Session getInstance() {
return this.session;
}
@PreDestroy
public void fecha() {
this.session.close();
}
}
isso é uma session por request… não deveria ter cache
Agora você vem falar pra mim!?
Eu sou Leigo no negóciooooooo jkkkkkk
Então, pensei no flush(), o que você acha?
se você comitou a transação já deveria ir pro banco de dados automaticamente, não precisa do flush…
habilita o show_sql e vê se as selects estão sendo feitas.
no hibernate.cfg.xml, coloque:
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
e veja em que momento os selects/inserts estão sendo feitos. daí vc tem uma idéia melhor se precisa fazer o flush ou não
Bom, na verdade sempre deixo isso ligado, gosto de ver os selects e inserts sempre…
Está fazendo select a cada requisição, sim… entretanto, verifiquei no banco, e está gravando, e não aparece, cash de página, não pode ser, testei em browsers diferentes… portanto, CACHE kkkk
Meu, realmente não sei o que fazer agora, estou sem saber por onde começar
se está aparecendo os selects a cada request NÃO é cache, pelo menos não do lado da aplicação… talvez seja algo do banco de dados…
qual você está usando?
MySQL 5…
Mas se ele está no banco as informaçlões já estão salvas no banco não pode ser do banco isso
o banco está em qual máquina? na local ou na remota?
as configurações do banco estão como localhost ou com o ip da máquina?
Estão como URL mysql.tal.com.
não estão local, estão remotas.
posta aqui o seu hibernate.cfg.xml (sem a senha do banco e coisas do tipo) das duas aplicações
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://mysql.quimiflex.net.br/database</property>
<property name="hibernate.connection.username">user</property>
<property name="hibernate.connection.password">password</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<mapping class="br.com.rhfactor.quimiflex.entidades.Categoria" />
<mapping class="br.com.rhfactor.quimiflex.entidades.Item" />
</session-factory>
</hibernate-configuration>
troque o dialect para:
org.hibernate.dialect.MySQL5InnoDBDialect
Pronto… feito, novamente…
Iniciei o server local, depois o server remoto.
No server remoto adicionei um registro no banco, OK, perfeito.
Então fui na minha página de listar local, nada do registro, fui em adicionar um novo registro (local), adicionei, e apareceu no listar os 2 registros perfeitamente.
se você tiver liberdade pra isso, apague a database do projeto, e deixe o hibernate criar de novo.
para isso, coloque a propriedade:
<property name="hibernate.hbm2ddl.auto" value="update" />
se não tiver liberdade, teste com uma outra database e veja se funciona
Portanto está fazendo uma select somente depois de um comit, correto?
não sei, aparece o select depois do insert no log? ou aparece toda vez que você executa a lista?
Bom, quando executa a lista (chamando a página de listar)
Hibernate: select this_.id as id17_0_, this_.nome as nome17_0_, this_.sigla as sigla17_0_ from UnidadeMedida this_
Mas novamente, fui direto no banco e adicionei um dado, fiz comit no banco e nada, continua fazendo select e não lendo os dados... Dropei todas as tabelas, e coloquei no hibernate.cfg.xml:
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
Mas do mesmo jeito não resolveu.
cara, não sei o que pode ser… se ele imprime o select no console, quer dizer que ele foi no banco… só se o banco está respondendo o select antigo por algum motivo…
se você rodar um MySQL local será que dá a mesma coisa?
Vou tentar isso hoje, mas primeiro vou instalar um server linux pra montar meu banco.
Bom, tudo foi resolvido integrando com o c3p0.
Adicionando a biblioteca e configurando no hibernate, deu tudo certo, agora posso alterar diretamente no banco que não há problemas com cache.