View Javadoc
1   package ejava.jpa.examples.query.jpaql;
2   
3   import static org.junit.Assert.*;
4   
5   import java.math.BigDecimal;
6   import java.text.NumberFormat;
7   import java.util.Arrays;
8   import java.util.Date;
9   import java.util.HashMap;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Map.Entry;
13  import java.util.stream.Collectors;
14  import java.util.stream.Stream;
15  
16  import javax.persistence.EntityManager;
17  import javax.persistence.Tuple;
18  import javax.persistence.TypedQuery;
19  
20  import org.slf4j.Logger;
21  import org.slf4j.LoggerFactory;
22  import org.hibernate.LazyInitializationException;
23  import org.junit.Test;
24  
25  import ejava.jpa.examples.query.Clerk;
26  import ejava.jpa.examples.query.Customer;
27  import ejava.jpa.examples.query.QueryBase;
28  import ejava.jpa.examples.query.Receipt;
29  import ejava.jpa.examples.query.Sale;
30  import ejava.jpa.examples.query.Store;
31  
32  public class JPAQLTest extends QueryBase {
33  	private static final Logger logger = LoggerFactory.getLogger(BulkQueryTest.class);
34  
35      private <T> List<T> executeQuery(String ejbqlString, Class<T> resultType) {
36          return executeQuery(ejbqlString, null, resultType);
37      }
38  
39      private <T> List<T> executeQuery(String ejbqlString, 
40              Map<String, Object> params, Class<T> resultType) {
41          TypedQuery<T> query = em.createQuery(ejbqlString, resultType);
42          logger.info("executing query: {}", ejbqlString);
43          if (params != null && !params.isEmpty()) {
44              StringBuilder text=new StringBuilder();
45              for(String key: params.keySet()) {
46                  Object param = params.get(key);
47                  text.append(key +"=" + param + ",");
48                  query.setParameter(key, param);
49              }
50              logger.info("   with params:[{}]", text);
51          }
52          List<T> objects = query.getResultList();
53          for(Object o: objects) {
54             logger.info("found result:{}", o);
55          }
56          return objects;
57      }
58      
59      /**
60       * This test demonstrates a single JPAQL query
61       */
62      @Test
63      public void testSimpleSelect() {
64          logger.info("*** testSimpleSelect() ***");
65          
66          TypedQuery<Customer> query = em.createQuery(
67          		"select object(c) from Customer as c", 
68          		Customer.class);
69          List<Customer> results = query.getResultList();
70          for (Customer result : results) {
71          	logger.info("found={}", result);
72          }
73          int rows = results.size();
74          assertTrue("unexpected number of customers", rows > 0);
75      }
76      
77      /**
78       * This test demonstrates getting results as a stream
79       */
80      @Test
81      public void testResultStream() {
82          logger.info("*** testStream() ***");
83          TypedQuery<Customer> query = em.createQuery(
84                  "select c from Customer as c", 
85                  Customer.class);
86          
87          Stream<Customer> s = query.getResultStream();
88          Map<String, Customer> resultMap = s.collect(
89                  Collectors.toMap(c->c.getFirstName()+c.getLastName(), c->c ));
90          
91          for (Entry<String, Customer> result : resultMap.entrySet()) {
92              logger.info("found={}", result);
93          }
94          int rows = resultMap.size();
95          assertTrue("unexpected number of customers", rows > 0);
96          
97      }
98      
99      
100     /**
101      * This test demonstrates querying for a non-entity. The property queried
102      * for is located off a path from the root query term.
103      */
104     @Test
105     public void testNonEntityQuery() {
106         logger.info("*** testNonEntityQuery() ***");
107         
108         TypedQuery<String> query = em.createQuery(
109                 "select c.lastName from Customer c", String.class);
110         List<String> results = query.getResultList();
111         assertTrue("no results", results.size() > 0);
112         for(String result : results) {
113             logger.info("lastName={}", result);
114         }
115     }
116 
117     /**
118      * This test demonstrates a query for multiple properties. In this
119      * version we will use a generic Object[] for the return type.
120      */
121     @Test
122     public void testMultiSelectObjectArray() {
123         logger.info("*** testMultiSelectObjectArray() ***");
124         
125         TypedQuery<Object[]> query = em.createQuery(
126                 "select c.firstName, c.hireDate from Clerk c", Object[].class);
127         List<Object[]> results = query.getResultList();
128         assertTrue("no results", results.size() > 0);
129         for(Object[] result : results) {
130             assertEquals("unexpected result length", 2, result.length);
131             String firstName = (String) result[0];
132             Date hireDate = (Date) result[1];
133             logger.info("firstName={} hireDate={}", firstName, hireDate);
134         }
135     }
136 
137     /**
138      * This query demonstrates a query for multiple properties -- same as above
139      * -- except this example used a Tuple return type and select aliases 
140      */
141     @Test
142     public void testMultiSelectTuple() {
143         logger.info("*** testMultiSelectTuple() ***");
144         
145         TypedQuery<Tuple> query = em.createQuery(
146                 "select c.firstName as firstName, c.hireDate as hireDate from Clerk c", Tuple.class);
147         List<Tuple> results = query.getResultList();
148         assertTrue("no results", results.size() > 0);
149         for(Tuple result : results) {
150             assertEquals("unexpected result length", 2, result.getElements().size());
151             String firstName = result.get("firstName", String.class);
152             Date hireDate = result.get("hireDate", Date.class);
153             logger.info("firstName={} hireDate={}", firstName, hireDate);
154         }
155     }
156     
157     /**
158      * This test provides another demonstration of selecting multiple properties --
159      * with this example using a constructor expression to return a typed 
160      * object for each result in the query.
161      */
162     @Test
163     public void testMultiSelectConstructor() {
164         logger.info("*** testMultiSelectConstructor() ***");
165         
166         TypedQuery<Receipt> query = em.createQuery(
167             String.format("select new %s(", Receipt.class.getName()) +
168             "s.id,s.buyerId,s.date, s.amount) " +
169             "from Sale s", Receipt.class);
170         List<Receipt> results = query.getResultList();
171         assertTrue("no results", results.size() > 0);
172         for(Receipt receipt : results) {
173             assertNotNull("no receipt", receipt);
174             logger.info("receipt={}", receipt);
175         }        
176     }
177 
178     /**
179      * This test provides an example of navigating a path formed by a 
180      * relationship. In this case the path used is a single element.
181      */
182     @Test
183     public void testPathExpressions() {
184         logger.info("*** testPathExpressions() ***");
185         
186         TypedQuery<Object[]> query = em.createQuery(
187                 "select s.id, s.store.name from Sale s", Object[].class);
188         List<Object[]> results = query.getResultList();
189         assertTrue("no results", results.size() > 0);
190         for(Object[] result : results) {
191             assertEquals("unexpected result length", 2, result.length);
192             Long id = (Long) result[0];
193             String name = (String) result[1];
194             logger.info("sale.id={}, sale.store.name={}", id, name);
195         }
196     }
197 
198     /**
199      * This test provides an example collection path using an INNER JOIN
200      */
201     @Test
202     public void testCollectionPathExpressionsInnerJoin() {
203         logger.info("*** testCollectionPathExpressionsInnerJoin ***");
204         
205         int rows = executeQuery(
206 //              "select sale.date from Clerk c INNER JOIN c.sales sale", 
207 //              "select sale.date from Clerk c, IN (c.sales) sale", 
208               "select sale.date from Clerk c JOIN c.sales sale", 
209                 Date.class).size();
210         assertTrue("unexpected number of sales", rows > 0);
211     }
212 
213     /**
214      * This test provides an example collection path using an LEFT OUTER JOIN
215      */
216     @Test
217     public void testOuterJoin() {
218         logger.info("*** testOuterJoin() ***");
219         
220         TypedQuery<Object[]> query = em.createQuery(
221             "select c.id, c.firstName, sale.amount " +
222             "from Clerk c " +
223 //            "LEFT OUTER JOIN c.sales sale",
224             "LEFT JOIN c.sales sale",
225             Object[].class);
226         List<Object[]> results = query.getResultList();
227         assertTrue("no results", results.size() > 0);
228         for(Object[] result : results) {
229             assertEquals("unexpected result length", 3, result.length);
230             Long id = (Long) result[0];
231             String name = (String) result[1];
232             BigDecimal amount = (BigDecimal) result[2];
233             logger.info("clerk.id={}, clerk.firstName={}, amount={}", 
234                     id, name, amount);
235         }
236     }
237     
238     /**
239      * This test demonstrates creating an explicit JOIN based on adhoc criteria
240      */
241     @Test
242     public void testExplicitJoin() {
243     	logger.info("*** testExplicitJoin ***");
244     	
245     	int rows = executeQuery(
246     		"select c from Sale s, Customer c " +
247     		"where c.id = s.buyerId", 
248     		Customer.class).size();
249         assertTrue("unexpected number of customers", rows > 0);
250     }
251     
252 
253     /**
254      * This test demonstrates the function of a JOIN FETCH to perform the 
255      * EAGER retrieval of entities as a side-effect of the query
256      */
257     @Test
258     public void testFetchJoin1() {        
259         logger.info("** testFetchJoin1() ***");
260         EntityManager em2 = createEm();
261         Store store = em2.createQuery(
262         		"select s from Store s JOIN s.sales " +
263         		"where s.name='Big Al''s'",
264         		Store.class).getSingleResult();
265         logger.info("em.contains({})", em2.contains(store));
266         em2.close();
267         try {
268         	store.getSales().get(0).getAmount();
269         	fail("did not trigger lazy initialization exception");
270         } catch (LazyInitializationException expected) {
271         	logger.info("caught expected exception:" + expected);
272         }
273     }
274     @Test
275     public void testFetchJoin2() {        
276         logger.info("** testFetchJoin2() ***");
277         EntityManager em2 = createEm();
278         Store store = em2.createQuery(
279         		"select s from Store s JOIN FETCH s.sales " +
280         		"where s.name='Big Al''s'",
281         		Store.class).getSingleResult();
282         logger.info("em.contains({})", em2.contains(store));
283         em2.close();
284        	store.getSales().get(0).getAmount();
285     }
286 
287     /**
288      * This test demonstrates how we can leverage multi-selects to extend a 
289      * JOIN FETCH beyond a single root anchoring point.
290      */
291     @Test
292     public void testMultiSelectEntities() {
293         logger.info("** testMultiSelectEntities() ***");
294 
295         EntityManager em2 = createEm();
296         Sale sale = em2.createQuery(
297         		"select sa as sale " +
298         		"from Sale sa " +
299         		"join sa.store st " +
300         		"join sa.clerks c " +        		
301         		"join fetch sa.clerks " +
302         		"join fetch sa.store " +
303         		"where st.name='Big Al''s' " +
304         		"and sa member of c.sales",
305         		Tuple.class).getResultList().get(0).get("sale", Sale.class);
306         em2.close();
307        	sale.getAmount();
308        	sale.getClerks().get(0).getFirstName();
309        	sale.getStore();
310     }
311     
312     /**
313      * This test demonstrates the use of DISTINCT to limit the results
314      * to only unique values
315      */
316     @Test
317     public void testDISTINCT() {
318         logger.info("*** testDISTINCT() ***");
319         
320         int rows = executeQuery(
321                 "select DISTINCT c.lastName from Customer c",
322                 Object[].class).size();
323         assertEquals("unexpected number of rows", 3, rows);
324         rows = executeQuery(
325                 "select DISTINCT c.firstName from Customer c",
326                 Object[].class).size();
327         assertEquals("unexpected number of rows for DISTINCT", 2, rows);
328     }
329     
330     
331     //where clauses
332     
333     
334     /**
335      * This test provides an example of an equality test in the where clause
336      */
337     @Test
338     public void testLiteral() {
339         logger.info("*** testLiteral() ***");
340         int rows = executeQuery(
341                 "select c from Customer c " +
342                 "where c.firstName='cat'",
343                 Customer.class).size();
344         assertEquals("unexpected number of rows:", 1, rows);
345     }
346 
347     @Test
348     public void testSpecialCharacter() {
349         logger.info("*** testSpecialCharacter() ***");
350         int rows = executeQuery(
351                 "select s from Store s " +
352                 "where s.name='Big Al''s'",
353                 Store.class).size();
354         assertEquals("unexpected number of rows", 1, rows);
355     }
356     
357     /**
358      * This test demonstrates the use of like in where clauses
359      */
360     @Test
361     public void testLike() {
362         logger.info("*** testLike() ***");
363         
364         int rows = executeQuery(
365                   "select c from Clerk c " +
366                   "where c.firstName like 'M%'",
367                   Clerk.class).size();
368         assertEquals("unexpected number of rows", 2, rows);
369         
370         Map<String,Object> params = new HashMap<String, Object>();
371         params.put("firstName", "M%");
372         rows = executeQuery(
373                 "select c from Clerk c " +
374                 "where c.firstName like :firstName",
375                 params,Clerk.class).size();
376         assertEquals("unexpected number of rows", 2, rows);        
377 
378         params = new HashMap<String, Object>();
379         params.put("firstName", "M");
380         rows = executeQuery(
381                 "select c from Clerk c " +
382                 "where c.firstName like concat(:firstName,'%')",
383                 params, Clerk.class).size();
384         assertEquals("unexpected number of rows", 2, rows);    
385         
386         rows = executeQuery(
387                 "select c from Clerk c " +
388                 "where c.firstName like '_anny'",
389                 Clerk.class).size();
390         assertEquals("unexpected number of rows", 1, rows);
391         
392     }
393     
394     /**
395      * This test provides a demonstration of using a math formual within the 
396      * where clause.
397      */
398     @Test
399     public void testFormulas() {
400         logger.info("*** testFormulas() ***");
401         
402         String jpaql = "select count(s) from Sale s " +
403                        "where (s.amount * :tax) > :amount";
404         TypedQuery<Number> query = em.createQuery(jpaql, Number.class)
405         		.setParameter("amount", new BigDecimal(10.00));
406                 
407         //keep raising taxes until somebody pays $10.00 in tax
408         double tax = 0.05;
409         for (;query.setParameter("tax", new BigDecimal(tax))
410         		   .getSingleResult().intValue()==0;
411         	  tax += 0.01) {
412         	logger.debug("tax={}", NumberFormat.getPercentInstance().format(tax));
413         }
414         logger.info("raise taxes to: {}", NumberFormat.getPercentInstance().format(tax));
415         
416         assertEquals("unexpected level for tax:" + tax, 0.07, tax, .01);
417     }
418     
419     /**
420      * This test provides a demonstration of using logical AND, OR, and NOT
421      * within a query where clause
422      */
423     @Test
424     public void testLogical() {
425         logger.info("*** testLogical() ***");
426         int rows = executeQuery(
427             "select c from Customer c " +
428             "where (c.firstName='cat' AND c.lastName='inhat')" +
429                 "OR c.firstName='thing'",
430             Customer.class).size();
431         assertEquals("unexpected number of rows", 3, rows);        
432 
433         rows = executeQuery(
434             "select c from Customer c " +
435             "where (NOT (c.firstName='cat' AND c.lastName='inhat')) " +
436                 "OR c.firstName='thing'",
437             Customer.class).size();
438         assertEquals("unexpected number of rows", 2, rows);        
439     }
440     
441     /**
442      * This test provides a demonstration for comparing two entities within
443      * a query
444      */
445     @Test
446     public void testEquality() {
447         logger.info("*** testEquality() ***");
448         
449         //get a clerk entity
450         Clerk clerk = em.createQuery(
451             	"select c from Clerk c where c.firstName = 'Manny'", 
452             	Clerk.class)
453                .getSingleResult();
454         
455         //find all sales that involve this clerk
456         List<Sale> sales = em.createQuery( 
457             "select s from Sale s " +
458             "JOIN s.clerks c " +
459             "where c = :clerk", 
460             Sale.class)
461 	            .setParameter("clerk", clerk)
462 	            .getResultList();
463 
464         for (Sale result : sales) {
465         	logger.info("found={}", result);
466         }
467         assertEquals("unexpected number of rows", 2, sales.size());
468     }
469     
470     /**
471      * This test provides an example of using between condition
472      */
473     @Test
474     public void testBetween() {
475         logger.info("*** testBetween() ***");
476         
477         String query = "select s from Sale s " +
478             "where s.amount BETWEEN :low AND :high";
479         Map<String, Object> params = new HashMap<String, Object>();
480         params.put("low", new BigDecimal(90.00));
481         params.put("high", new BigDecimal(110.00));
482                 
483         int rows = executeQuery(query, params, Sale.class).size();    
484         assertEquals("unexpected number of rows", 1, rows);
485 
486         query = "select s from Sale s " +
487                 "where s.amount NOT BETWEEN :low AND :high";
488         params = new HashMap<String, Object>();
489         params.put("low", new BigDecimal(90.00));
490         params.put("high", new BigDecimal(110.00));
491                     
492         rows = executeQuery(query, params, Sale.class).size();    
493         assertEquals("unexpected number of rows", 1, rows);
494     }
495     
496     /**
497      * This test provides a demonstration of testing for a null value.
498      */
499     @Test
500     public void testIsNull() {
501         logger.info("*** testIsNull() ***");
502         
503         String query = "select s from Sale s " +
504             "where s.store IS NULL";
505                 
506         int rows = executeQuery(query, Sale.class).size();    
507         assertEquals("unexpected number of rows", 0, rows);
508 
509         query = "select s from Sale s " +
510             "where s.store IS NOT NULL";
511             
512         rows = executeQuery(query, Sale.class).size();    
513         assertEquals("unexpected number of rows", 2, rows);
514     }
515 
516     /**
517      * This test provides an example of testing whether the collection
518      * is empty
519      */
520     @Test
521     public void testIsEmpty() {
522         logger.info("*** testIsEmpty() ***");
523         
524         String query = "select c from Clerk c " +
525             "where c.sales IS EMPTY";
526                 
527         int rows = executeQuery(query, Clerk.class).size();    
528         assertEquals("unexpected number of rows", 1, rows);
529 
530         query = "select c from Clerk c " +
531             "where c.sales IS NOT EMPTY";
532             
533         rows = executeQuery(query, Clerk.class).size();    
534         assertEquals("unexpected number of rows", 2, rows);
535     }
536 
537     /**
538      * This test provides a demonstration of testing membership in 
539      * a collection.
540      */
541     @Test
542     public void testMemberOf() {
543         logger.info("*** testMemberOf() ***");
544         
545         //get a clerk entity
546         Clerk clerk = em.createQuery(
547             "select c from Clerk c where c.firstName = 'Manny'",
548             Clerk.class)
549               .getSingleResult();
550         
551         //find all sales that involve this clerk
552         List<Sale> sales = em.createQuery(
553             "select s from Sale s " +
554             "where :clerk MEMBER OF s.clerks", Sale.class)
555                 .setParameter("clerk", clerk)
556                 .getResultList();
557         
558         for (Sale result : sales) {
559         	logger.info("found={}", result);
560         }
561         assertEquals("unexpected number of rows", 2, sales.size());
562     }
563 
564     /**
565      * This test provides a demonstration of using an explicit subquery
566      */
567     @Test
568     public void testSubqueries() {
569        logger.info("*** testSubqueries() ***");   
570        
571        List<Customer> results = executeQuery(
572                "select c from Customer c " +
573                "where c.id IN " +
574                    "(select s.buyerId from Sale s " +
575                     "where s.amount > 100)",
576               Customer.class);       
577        assertEquals("unexpected number of rows", 1, results.size());
578     }
579     
580     
581     /**
582      * This test provides a demonstration for using the ALL subquery
583      * result evaluation.
584      */
585     @Test
586     public void testAll() {
587         logger.info("*** testAll() ***");  
588         
589         //executeQuery("select s from Sale s", Sale.class);
590         
591 		List<Clerk> results = executeQuery(
592                 "select c from Clerk c " +
593                 "where 125 < ALL " +
594                 "   (select s.amount from c.sales s)",
595                Clerk.class);       
596         assertEquals("unexpected number of rows", 2, results.size());
597         
598         results = executeQuery(
599                 "select c from Clerk c " +
600                 "where 125 > ALL " +
601                 "   (select s.amount from c.sales s)",
602                Clerk.class);       
603         assertEquals("unexpected number of rows", 1, results.size());
604     }
605     
606     /**
607      * This test provides a demonstration for using the ANY subquery
608      * result evaluation
609      */
610     @Test
611     public void testAny() {
612 		List<Clerk> results = executeQuery(
613                 "select c from Clerk c " +
614                 "where 125 < ANY " +
615                 "   (select s.amount from c.sales s)",
616                Clerk.class);       
617         assertEquals("unexpected number of rows", 2, results.size());
618 
619 		results = executeQuery(
620                 "select c from Clerk c " +
621                 "where 125 > ANY " +
622                 "   (select s.amount from c.sales s)",
623                Clerk.class);       
624         assertEquals("unexpected number of rows", 1, results.size());
625      }
626 
627     
628     
629     /**
630      * This test method demonstrates several date functions
631      */
632     @Test
633     public void testStringFunctions() {
634         logger.info("*** testStringFunctions() ***");
635 
636         int rows = executeQuery(
637                 "select c from Customer c " +
638                 "where c.firstName='CAT'",
639                 Customer.class).size();
640         assertEquals("unexpected number of rows", 0, rows);
641 
642         rows = executeQuery(
643                 "select c from Customer c " +
644                 "where c.firstName=LOWER('CAT')",
645                 Customer.class).size();
646         assertEquals("unexpected number of rows", 1, rows);
647     
648         rows = executeQuery(
649                 "select UPPER(c.firstName) from Customer c " +
650                 "where c.firstName=LOWER('CAT')",
651                 String.class).size();
652         assertEquals("unexpected number of rows", 1, rows);
653     
654         rows = executeQuery(
655                 "select TRIM(LEADING 'c' FROM c.firstName) from Customer c " +
656                 "where c.firstName='cat'",
657                 String.class).size();
658         assertEquals("unexpected number of rows", 1, rows);
659         
660         rows = executeQuery(
661                 "select c from Customer c " +
662                 "where CONCAT(CONCAT(c.firstName,' '),c.lastName) ='cat inhat'",
663                 Customer.class).size();
664         assertEquals("unexpected number of rows", 1, rows);
665         
666         rows = executeQuery(
667                 "select c from Customer c " +
668                 "where LENGTH(c.firstName) = 3",
669                 Customer.class).size();
670         assertEquals("unexpected number of rows", 1, rows);
671         
672         rows = executeQuery(
673                 "select c from Customer c " +
674                 "where LOCATE('cat',c.firstName,2) > 0",
675                 Customer.class).size();
676         assertEquals("unexpected number of rows", 0, rows);        
677 
678         rows = executeQuery(
679                 "select c from Customer c " +
680                 "where LOCATE('at',c.firstName,2) > 1",
681                 Customer.class).size();
682         assertEquals("unexpected number of rows", 1, rows);        
683 
684         rows = executeQuery(
685                 "select SUBSTRING(c.firstName,2,2) from Customer c " +
686                 "where c.firstName = 'cat'",
687                 String.class).size();
688         assertEquals("unexpected number of rows", 1, rows);
689         
690         rows = executeQuery(
691                 "select c from Customer c " +
692                 "where SUBSTRING(c.firstName,2,2) = 'at'",
693                 Customer.class).size();
694         assertEquals("unexpected number of rows", 1, rows);        
695     }
696 
697     /**
698      * This test method demonstrates using date functions.
699      */
700     @Test
701     public void testDates() {        
702         logger.info("*** testDates() ***");
703 
704         int rows = executeQuery(
705                 "select s from Sale s " +
706                 "where s.date < CURRENT_DATE",
707                 Sale.class).size();
708         assertEquals("unexpected number of rows", 2, rows);
709         
710         rows = executeQuery(
711                 "select s from Sale s " +
712                 "where s.date = CURRENT_DATE",
713                 Sale.class).size();
714         assertEquals("unexpected number of rows", 0, rows);
715 
716         rows = em.createQuery(
717                 "update Sale s " +
718                 "set s.date = CURRENT_DATE").executeUpdate();
719         assertEquals("unexpected number of rows", 2, rows);
720         
721         em.getTransaction().commit();
722         em.clear(); //remove stale objects in cache
723         
724         rows = executeQuery(
725                 "select s from Sale s " +
726                 "where s.date = CURRENT_DATE",
727                 Sale.class).size();
728         assertEquals("unexpected number of rows", 2, rows);
729     }
730     
731     
732     /**
733      * This test method provides a demonstration of order by capability.
734      */
735     @Test
736     public void testOrderBy() {
737         logger.info("*** testOrderBy() ***");
738 
739         List<Sale> results = executeQuery(
740             "select s from Sale s ORDER BY s.amount ASC", Sale.class); 
741         assertEquals("unexpected number of rows", 2, results.size());
742         assertEquals("unexpected first element", 
743                 100, 
744                 results.get(0).getAmount().intValue());
745         assertEquals("unexpected first element", 
746                 150, 
747                 results.get(1).getAmount().intValue());
748 
749         
750         results = executeQuery(
751                 "select s from Sale s ORDER BY s.amount DESC", Sale.class); 
752         assertEquals("unexpected number of rows", 2, results.size());
753         assertEquals("unexpected first element", 
754                 150, 
755                 results.get(0).getAmount().intValue());
756         assertEquals("unexpected first element", 
757                 100, 
758                 results.get(1).getAmount().intValue());
759     }
760 
761     
762     /**
763      * This test provides a demonstration of the COUNT aggregate function
764      */    
765     @Test
766     public void testCount() {        
767         logger.info("*** testCount() ***");
768 
769         List<Number> results= executeQuery(
770                 "select COUNT(s) from Sale s", Number.class);
771         assertEquals("unexpected number of rows", 1, results.size());
772         assertEquals("unexpected result", 2, results.get(0).intValue());
773     }
774     
775     /**
776      * This test provides a demonstration of the MIN and MAX aggregate functions
777      */
778     @Test
779     public void testMaxMin() {        
780         logger.info("*** testMaxMin() ***");
781 
782         List<Number> results= executeQuery(
783                 "select max(s.amount) from Sale s", Number.class);
784         assertEquals("unexpected number of rows", 1, results.size());
785         assertEquals("unexpected result", 150, results.get(0).intValue());
786         
787         results= executeQuery(
788                 "select min(s.amount) from Sale s", Number.class);
789         assertEquals("unexpected number of rows", 1, results.size());
790         assertEquals("unexpected result", 100, results.get(0).intValue());
791     }
792 
793     /**
794      * This test provides a demonstration of the SUM and AVE aggregate functions
795      */
796     @Test
797     public void testSumAve() {        
798         logger.info("*** testSumAve() ***");
799 
800         List<Number> results= executeQuery(
801             "select sum(s.amount) from Sale s", Number.class);
802         assertEquals("unexpected number of rows", 1, results.size());
803         assertEquals("unexpected result", 250, results.get(0).intValue());
804         
805         results= executeQuery(
806                 "select avg(s.amount) from Sale s", Number.class);
807         assertEquals("unexpected number of rows", 1, results.size());
808         assertEquals("unexpected result", 125, results.get(0).intValue());
809     }
810     
811     /**
812      * This test method provides an example of using group by 
813      */
814     @Test
815     public void testGroupBy() {
816         logger.info("*** testGroupBy() ***");
817     	
818         List<Object[]> results= em.createQuery(
819                 "select c, COUNT(s) from Clerk c " +
820                 "LEFT JOIN c.sales s " +
821                 "GROUP BY c", Object[].class)
822                 .getResultList();
823         for (Object[] result : results) {
824         	logger.info("found={}", Arrays.toString(result));
825         }
826         assertEquals("unexpected number of rows", 3, results.size());
827     }
828     
829 
830     /**
831      * This test provides an example usage of the HAVING aggregate query
832      * function.
833      */
834     @Test
835     public void testHaving() {
836         logger.info("*** testHaving() ***");
837     	
838         List<Object[]> results= em.createQuery(
839                 "select c, COUNT(s) from Clerk c " +
840                 "LEFT JOIN c.sales s " +
841                 "GROUP BY c " +
842                 "HAVING COUNT(s) <= 1", Object[].class)
843                 .getResultList();
844         for (Object[] result : results) {
845         	logger.info("found={}", Arrays.toString(result));
846         }
847         assertEquals("unexpected number of rows", 2, results.size());
848     }
849 }