<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>blog.caelum.com.br &#187; detachedcriteria</title>
	<atom:link href="http://blog.caelum.com.br/tag/detachedcriteria/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.caelum.com.br</link>
	<description>blog dos desenvolvedores da Caelum</description>
	<lastBuildDate>Thu, 09 Feb 2012 13:04:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Divisions com Hibernate: uso avançado da Criteria API</title>
		<link>http://blog.caelum.com.br/divisions-com-hibernate-uso-avancado-da-criteria-api/</link>
		<comments>http://blog.caelum.com.br/divisions-com-hibernate-uso-avancado-da-criteria-api/#comments</comments>
		<pubDate>Thu, 11 Sep 2008 12:57:00 +0000</pubDate>
		<dc:creator>Lucas Cavalcanti</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[banco de dados]]></category>
		<category><![CDATA[criteria]]></category>
		<category><![CDATA[detachedcriteria]]></category>
		<category><![CDATA[division]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[hql]]></category>
		<category><![CDATA[jpa]]></category>

		<guid isPermaLink="false">http://blog.caelum.com.br/?p=316</guid>
		<description><![CDATA[Existe uma operação, não muito conhecida, mas muitas vezes necessária, em bancos de dados chamada divisão (division). Essa operação representa o seguinte tipo de consulta: Selecione os alunos que fizeram todos os cursos. Selecione os autores em que todos os seus livros têm mais de 200 páginas. E assim por diante. Esse tipo de consulta <a href="http://blog.caelum.com.br/divisions-com-hibernate-uso-avancado-da-criteria-api/#more-316'" class="more-link">more &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>Existe uma operação, não muito conhecida, mas muitas vezes necessária, em bancos de dados chamada  <strong>divisão</strong> (<em>division</em>). Essa operação representa o seguinte tipo de consulta: Selecione os alunos que fizeram todos os cursos. Selecione os autores em que <strong>todos</strong> os seus livros têm mais de 200 páginas. E assim por diante.</p>
<p>Esse tipo de consulta precisa de alguns recursos avançados do SQL, então antes de mostrar como implementá-la vamos ver como implementar consultas um pouco mais simples, usando a <a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/criterion/Subqueries.html">Subqueries</a> e a <a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/criterion/DetachedCriteria.html">DetachedCriteria</a>, que nos possibilitam consultas bastante poderosas usando a api da <a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/criterion/package-frame.html">Criteria</a>.</p>
<p>Bom, vamos começar com três entidades: <code>Aluno</code>, <code>Curso</code>, e um relacionamento de muitos pra muitos entre eles representado pela entidade <code>Matrícula</code>.</p>
<p>Vamos pensar um pouquinho como fazer a seguinte consulta: &#8220;<em>Selecionar todos os alunos que estejam cursando <strong>Matemática</strong> ou <strong>Português</strong></em>&#8220;. Pensando em banco de dados, podemos fazer um <em>join</em> entre Alunos e Matrículas, e selecionar as linhas em que o curso é <strong>matemática</strong> ou é <strong>português</strong>. Precisamos também evitar que a busca retorne alunos repetidos. Vamos fazer isso com <code>Criteria</code>, recebendo a lista dos cursos que eu quero que o aluno esteja cursando algum deles: </p>
<pre class="brush: java; title: ; notranslate">
public List&lt;Aluno&gt; alunosCursandoAlgumDessesCursos(List&lt;Curso&gt; cursos) {
  Criteria criteria = session.createCriteria(Aluno.class);
  //join com as matrículas
  criteria.createCriteria(&quot;matriculas&quot;, &quot;m&quot;);

  //usando a disjunction para fazer um 'ou' entre vários elementos
  Disjunction ou = Restrictions.disjunction();
  for (Curso curso : cursos) {
    ou.add(Restrictions.eq(&quot;m.curso&quot;, curso);
  }
  criteria.add(ou);

  //eliminando resultados repetidos
  criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
  return criteria.list();
}
</pre>
<p>Ou podemos fazer algo bem mais interessante, que é usar a restrição <a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/criterion/Restrictions.html#in(java.lang.String,%20java.util.Collection)">in</a>, que retorna verdadeiro se a propriedade dada é igual a algum dos elementos da coleção que passarmos pra ela. Nesse caso trocaríamos o <code>Disjunction</code> por simplesmente: </p>
<pre class="brush: java; title: ; notranslate">
criteria.add(Restrictions.in(&quot;m.curso&quot;, cursos));
</pre>
<p>Bem fácil! Agora vamos mudar só um pouquinho a consulta para: &#8220;<em>Selecione todos os alunos que estiverem cursando Português <b>E</b> Matemática</em>&#8220;. Poderíamos inocentemente mudar a <code>Disjunction</code> para <code>Conjunction</code> no método anterior. Mas isso não funciona! Por quê? Porque se fizermos isso, estaríamos mudando a consulta para algo do tipo: &#8220;<em>Selecione os alunos que tenham uma matrícula que é em Português e em Matemática ao mesmo tempo</em>&#8220;. E isso não é possível. Temos que mudar essa consulta para algo do tipo: &#8220;<em>Selecione todos os alunos para os quais exista uma matricula no curso Português e exista uma matrícula no curso Matemática</em>&#8220;.</p>
<p>Existe uma operação em SQL que faz exatamente isso: o <b>exists</b>. Ela retorna verdadeiro se a subconsulta que estiver depois dela retornar algum resultado. Para fazer isso precisamos então criar subconsultas em <code>Criteria</code>, e o jeito de fazer isso é usando a classe <a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/criterion/Subqueries.html">Subqueries</a>, que fabrica <code>Criterions</code> que envolvem a criação de subconsultas.</p>
<p>Para usar qualquer método da Subqueries precisamos de uma <a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/criterion/DetachedCriteria.html">DetachedCriteria</a>. Essa <code>DetachedCriteria</code> é um tipo especial de <code>Criteria</code> que não precisa da session do hibernate para ser criada. Dentro dela temos acesso a todos os alias e propriedades da <code>Criteria</code> principal, e o uso é o mesmo que faríamos para Criterias normais.</p>
<p>Já que temos a Subqueries na mão, vamos implementar a consulta, recebendo a lista dos cursos que queremos que o aluno esteja matriculado em todos eles:</p>
<pre class="brush: java; title: ; notranslate">
public List&lt;Aluno&gt; alunosCursandoTodosEssesCursos(List&lt;Curso&gt; cursos) {
  Criteria criteria = session.createCriteria(Aluno.class, &quot;a&quot;);
  Conjunction e = Restrictions.conjunction();
  for (Curso c : cursos) {
    e.add(Subqueries.exists(
      DetachedCriteria.forClass(Matricula.class, &quot;m&quot;)
        .setProjection(Projections.id())
        .add(Restrictions.eqProperty(&quot;a.id&quot;, &quot;m.aluno.id&quot;))
        .add(Restrictions.eq(&quot;m.curso&quot;,c))));
  }
  criteria.add(e);
  return criteria.list();
}
</pre>
<p>Ou seja, queremos que exista uma matrícula do aluno da <code>Criteria</code> principal para cada curso da lista passada.</p>
<p>Mas vamos pensar no seguinte: Essa lista de cursos provavelmente veio de outra consulta no banco, por que não usar essa consulta, ao invés da lista de cursos?! O jeito de fazer isso é usando o operador division que falamos no começo do post. Ele é meio complicado de implementar, pois você tem que pensar meio ao contrário do normal. Por exemplo, para implementar a consulta &#8220;<em>Selecione os alunos que estão matriculados em todos os cursos</em>&#8221; precisamos transformá-la para: &#8220;<em>Selecione os alunos para os quais não exista nenhum curso para o qual não exista matrícula desse aluno para esse curso</em>&#8220;, ou seja: um aluno que não exista nenhum curso em que ele não esteja matriculado. É estranho mas é assim mesmo que é feito. A Subqueries também possui o método <code>notExists</code>, então podemos fazer a seguinte consulta, que traz os alunos que fazem todos os cursos:</p>
<pre class="brush: java; title: ; notranslate">
public List&lt;Aluno&gt; alunosCursandoTodosOsCursos() {
  Criteria criteria = session.createCriteria(Aluno.class, &quot;a&quot;);
  criteria.add(Subqueries.notExists(
      DetachedCriteria.forClass(Curso.class, &quot;c&quot;)
        .setProjection(Projections.id())
        .add(Subqueries.notExists(
            DetachedCriteria.forClass(Matricula.class, &quot;m&quot;)
              .setProjection(Projections.id())
              .add(Restrictions.eqProperty(&quot;m.curso.id&quot;, &quot;c.id&quot;))
              .add(Restrictions.eqProperty(&quot;m.aluno.id&quot;, &quot;a.id&quot;)
        ))
      ));
  return criteria.list();
}
</pre>
<p>Não é um bicho de sete cabeças, mas também não é nada trivial. O código fica meio poluído por causa das chamadas estáticas, mas se você fizer o <code>import static</code> dos métodos a coisa melhora um pouquinho.</p>
<p>As restrições que você tinha colocado para buscar a lista de cursos dos métodos anteriores, você pode colocar na <code>DetachedCriteria</code> de<code> Cursos</code>, que vai funcionar do jeito que é esperado. Por exemplo: &#8220;<em>Selecione os alunos que estejam matriculados em um curso noturno</em>&#8221; vira &#8220;<em>Selecione os alunos para os quais não exista algum curso noturno em que ele não esteja matriculado</em>&#8220;. Mais ainda: você pode colocar restrições pertinentes na <code>DetachedCriteria</code> da matrícula, que também vai funcionar da forma esperada. Por exemplo: &#8220;<em>Selecione os alunos que estejam com a matricula paga em todos os cursos</em>&#8221; vira &#8220;<em>Selecione os alunos para os quais não existe algum curso em que não exista matrícula paga nesse curso</em>&#8220;. </p>
<p>Existem muitos casos em que o operador division salva sua vida então, mesmo que ele seja meio complicadinho, é bom saber que ele existe e ter uma boa referência de como implementá-lo =).</p>
<p>Além da Subqueries, existe outra classe muito útil que fabrica <code>Criterion</code>s e <code>Projection</code>s relacionados a uma propriedade fixa: a <a href="http://docs.jboss.org/hibernate/core/3.3/api/org/hibernate/criterion/Property.html">Property</a>. Vale a pena olhar o javadoc do hibernate e ver a quantidade de opções de consultas que temos disponíveis. Existe <a href="http://opensource.atlassian.com/projects/hibernate/browse/HHH-993">um bug</a> no hibernate que te obriga a setar uma <code>Projection</code> nas <code>DetachedCriterias</code> quando usadas dentro das Subqueries, se isso não é feito o hibernate nos presenteia com uma <code>NullPointerException</code>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.caelum.com.br/divisions-com-hibernate-uso-avancado-da-criteria-api/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
	</channel>
</rss>

