引用:html
http://blog.csdn.net/yingxiake/article/details/51016234java
http://blog.csdn.net/yingxiake/article/details/51016234spring
http://www.cnblogs.com/zj0208/p/6008627.htmlsql
Query的使用:数据库
在JPA 2.0 中咱们可使用entityManager.createNativeQuery()来执行原生的SQL语句。 但当咱们查询结果没有对应实体类时,query.getResultList()返回的是一个List<Object[]>。也就是说每行的数据被做为一个对象数组返回。
数组
常见的用法是这样的:session
public void testNativeQuery(){ Query query = entityManager.createNativeQuery("select id, name, age from t_user"); List rows = query.getResultList(); for (Object row : rows) { Object[] cells = (Object[]) row; System.out.println("id = " + cells[0]); System.out.println("name = " + cells[1]); System.out.println("age = " + cells[2]); } }
这样用会使代码很是不容易让人理解, 究竟下标为0的元素究竟是什么, 不去数查询语句是不知道的,并且一旦查询语句被调整,Java代码也要一块儿调整。这时候咱们想若是返回的是Map的话,用起来会清晰的多。
惋惜的是JPA的API中并无提供这样的设置。其实不少JPA的底层实现都是支持返回Map对象的。例如:
EclipseLink的query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
Hibernate的.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
因此,若是咱们想要返回Map而且肯定底层用的是某一种JPA的实现时咱们能够退而求其次, 牺牲跨实现的特性来知足咱们的需求:post
public void testNativeQuery(){ Query query = entityManager.createNativeQuery("select id, name, age from t_user"); query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); List rows = query.getResultList(); for (Object obj : rows) { Map row = (Map) obj; System.out.println("id = " + row.get("ID")); System.out.println("name = " + row.get("NAME")); System.out.println("age = " + row.get("AGE")); } }
或者
public List<Map> findMapBySql(String sqlStr) {
Session session = getEntityManager().unwrap(Session.class);
return session.createSQLQuery(sqlStr).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
}
这里须要注意的是, 用Map确定要比用Object数组来的效率低。因此你要看性能降低是否在可接受范围内。再就是在个人Hibernate 4.2.x的环境下,不管你原生SQL中写的是大写字母仍是小写字母,返回的字段名都是大写的。固然你能够经过自定义ResultTransformer的形式对字段名进行必定的处理, 甚至是返回本身须要的POJO。性能
还有一种更简单的办法:spa
Query query = em.createNativeQuery(sql,java.util.Map.class);
这样就能够直接返回Map格式的结果集了。
@Query的使用
1. 一个使用@Query注解的简单例子
@Query(value = "select name,author,price from Book b where b.price>?1 and b.price<?2")
List<Book> findByPriceRange(long price1, long price2);
2. Like表达式
@Query(value = "select name,author,price from Book b where b.name like %:name%")
List<Book> findByNameMatch(@Param("name") String name);
3. 使用Native SQL Query
所谓本地查询,就是使用原生的sql语句(根据数据库的不一样,在sql的语法或结构方面可能有所区别)进行查询数据库的操做。
@Query(value = "select * from book b where b.name=?1", nativeQuery = true) List<Book> findByName(String name);
4. 使用@Param注解注入参数
@Query(value = "select name,author,price from Book b where b.name = :name AND b.author=:author AND b.price=:price")
List<Book> findByNamedParam(@Param("name") String name, @Param("author") String author, @Param("price") long price);
5. SPEL表达式(使用时请参考最后的补充说明)
'#{#entityName}'值为'Book'对象对应的数据表名称(book)。
public interface BookQueryRepositoryExample extends Repository<Book, Long>{
@Query(value = "select * from #{#entityName} b where b.name=?1", nativeQuery = true)
List<Book> findByName(String name);
}
6. 一个较完整的例子
public interface BookQueryRepositoryExample extends Repository<Book, Long> { @Query(value = "select * from Book b where b.name=?1", nativeQuery = true) List<Book> findByName(String name);// 此方法sql将会报错(java.lang.IllegalArgumentException),看出缘由了吗,若没看出来,请看下一个例子 @Query(value = "select name,author,price from Book b where b.price>?1 and b.price<?2") List<Book> findByPriceRange(long price1, long price2); @Query(value = "select name,author,price from Book b where b.name like %:name%") List<Book> findByNameMatch(@Param("name") String name); @Query(value = "select name,author,price from Book b where b.name = :name AND b.author=:author AND b.price=:price") List<Book> findByNamedParam(@Param("name") String name, @Param("author") String author, @Param("price") long price); }
7. 解释例6中错误的缘由:
由于指定了nativeQuery = true,即便用原生的sql语句查询。使用java对象'Book'做为表名来查天然是不对的。只需将Book替换为表名book。
@Query(value = "select * from book b where b.name=?1", nativeQuery = true) List<Book> findByName(String name);
8. 在这里咱们说下,spring data jpa的查询策略,spring data jpa能够利用建立方法进行查询,也能够利用@Query注释进行查询,那么若是在命名规范的方法上使用了@Query,那spring data jpa是执行咱们定义的语句进行查询,仍是按照规范的方法进行查询呢?看下查询策略
查询策略的配置能够在配置query-lookup-strategy,例如这样
<jpa:repositories base-package="com.liuxg.**.dao" repository-impl-postfix="Impl" query-lookup-strategy = "create-if-not-found" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" > </jpa:repositories>
它有三种值能够配置
create-if-not-found(默认):若是经过 @Query指定查询语句,则执行该语句,若是没有,则看看有没有@NameQuery指定的查询语句,若是尚未,则经过解析方法名进行查询
create:经过解析方法名字来建立查询。即便有 @Query,@NameQuery都会忽略
use-declared-query:经过执行@Query定义的语句来执行查询,若是没有,则看看有没有经过执行@NameQuery来执行查询,尚未则抛出异常
@Query就先看到这里,下次再了解下怎么拓展spring data jpa 接口,例如我既想用sping data jpa的接口,但是我又想本身定义一些接口,咱们把他们合二为一呢??
补充说明(2017-01-12):
有同窗提出来了,例子5中用'#{#entityName}'为啥取不到值啊?
先来讲一说'#{#entityName}'究竟是个啥。从字面来看,'#{#entityName}'不就是实体类的名称么,对,他就是。
实体类Book,使用@Entity注解后,spring会将实体类Book归入管理。默认'#{#entityName}'的值就是'Book'。
可是若是使用了@Entity(name = "book")来注解实体类Book,此时'#{#entityName}'的值就变成了'book'。
到此,事情就明了了,只须要在用@Entity来注解实体类时指定name为此实体类对应的表名。在原生sql语句中,就能够把'#{#entityName}'来做为数据表名使用。