在 Spring 引导中编写自定义查询
writing custom queries in Spring boot
我最近开始使用Spring引导,并且运行陷入了一点问题。以前,当我只是在休眠和 JPA 中使用 Spring 数据时,我可以创建一个 hibernate.cfg.xml 文件,该文件将提供一堆可以传递给配置对象的配置,然后最终创建一个 SessionFactory 对象这将创建一个会话对象,该对象可用于将查询传递给休眠:
package util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
Configuration configuration = new Configuration().configure("hibernate.cfg.xml"); return configuration.buildSessionFactory( new
StandardServiceRegistryBuilder().applySettings( configuration.getProperties() ).build() );
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() { return sessionFactory; }
}
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hello-world</property>
<property name="connection.username">root</property>
<property name="connection.password">password</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- Create/update tables automatically using mapping metadata -->
<property name="hbm2ddl.auto">update</property>
<!-- Use Annotation-based mapping metadata -->
<mapping class="entity.Author"/>
<mapping class="entity.Article"/>
</session-factory>
</hibernate-configuration>
Main.java
public class HelloWorldClient {
public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction txn = session.getTransaction();
EntityManagerFactory emf = Persiscance.createEntityManagerFactory("hibernate.cfg.xml");
EntityManager em = emf.createEntityManager();
EntityTransaction txn = em.getTransaction();
try {
txn.begin();
Author author = new Author("name", listOfArticlesWritten);
Article article = new Article("Article Title", author);
session.save(author);
session.save(article);
Query query = session.createQuery("select distinct a.authorName from Article s
where s.author like "Joe%" and title = 'Spring boot');
List<Article> articles = query.list();
txn.commit();
} catch(Exception e) {
if(txn != null) { txn.rollback(); }
e.printStackTrace();
} finally {
if(session != null) { session.close(); } }
}
}
这就是问题所在。我不知道如何避免为自定义查询创建 hibernate.cfg.xml 文件或会话工厂。在 Spring 指南页面和我完成的一些教程中,他们采用了他们的 DAO 并扩展了 CrudRepository 接口,该接口已经提供了一堆方法,以及一种命名方法的方法,以便 Hibernate 可以构建sql 独自一人。
我想要完成的,至少在这个 post 是能够在 spring 启动时执行上面的查询。我可以创建一个属性文件
application.properties
# ===============================
# = DATA SOURCE
# ===============================
# Set here configurations for the database connection
spring.datasource.url = jdbc:mysql://localhost:3306/spring-boot-demo
spring.datasource.username = test
spring.datasource.password = test
# Mysql connector
spring.datasource.driverClassName = com.mysql.jdbc.Driver
# ===============================
# = JPA / HIBERNATE
# ===============================
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Ddl auto must be set to "create" to ensure that Hibernate will run the
# import.sql file at application startup
#create-drop| update | validate | none
spring.jpa.hibernate.ddl-auto = update
# SQL dialect for generating optimized queries
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
# ===============================
# = THYMELEAF
# ===============================
spring.thymeleaf.cache = false
#debug=true
我可以将除映射之外的所有内容移动到属性文件,但是我不清楚如何编写查询,因为不再有会话对象。
在 spring 引导应用程序中您无需创建 xml 配置,您必须配置 java 文件本身。
检查这个例子,
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.annotations.common.util.impl.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
@Configuration
public class DatabaseConfig {
private org.jboss.logging.Logger log = LoggerFactory.logger(DatabaseConfig.class);
@Value("${db.driver}")
private String DB_DRIVER;
@Value("${db.username}")
private String DB_USERNAME;
@Value("${db.password}")
private String DB_PASSWORD;
@Value("${db.url}")
private String DB_URL;
@Value("${hibernate.dialect}")
private String HIBERNATE_DIALECT;
@Value("${hibernate.show_sql}")
private String HIBERNATE_SHOW_SQL;
@Value("${hibernate.hbm2ddl.auto}")
private String HIBERNATE_HBM2DDL_AUTO;
@Value("${entitymanager.packagesToScan}")
private String ENTITYMANAGER_PACKAGES_TO_SCAN;
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = null;
try {
dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(DB_DRIVER);
dataSource.setUrl(DB_URL);
dataSource.setUsername(DB_USERNAME);
dataSource.setPassword(DB_PASSWORD);
} catch (Exception e) {
e.getMessage();
}
return dataSource;
}
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan(ENTITYMANAGER_PACKAGES_TO_SCAN);
Properties hibernateProps = new Properties();
hibernateProps.put("hibernate.dialect", HIBERNATE_DIALECT);
hibernateProps.put("hibernate.show_sql", HIBERNATE_SHOW_SQL);
hibernateProps.put("hibernate.hbm2ddl.auto", HIBERNATE_HBM2DDL_AUTO);
sessionFactoryBean.setHibernateProperties(hibernateProps);
return sessionFactoryBean;
}
@Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
}
如果你使用 Spring Boot + Spring Data JPA,然后你配置你的数据源(你现在把它放在 hibernate.cfg.xml)使用 spring.datasource.*
properties.
进入 application.properties
这应该会自动为您创建一个实体管理器。如果需要使用查询,可以使用Spring Data JPA的repositories,例如:
public interface ArticleRepository extends JpaRepository<Article, Long> {
@Query("select s from Article s where s.author like ?1 and s.title = ?2")
List<Article> findByAuthorAndTitle(String author, String title);
}
现在您可以自动装配存储库并使用给定的查询,如下所示:
List<Article> articles = repository.findByAuthorAndTitle("Joe%", "Spring boot");
如果您确实需要自定义查询,可以使用 JPA 中的 Predicate/Criteria API。 Spring 提供了这些谓词的包装版本,称为 Specifications。
为此,您可以使用另一个名为 JpaSpecificationExecutor<Article>
的接口扩展 ArticleRepository
。这会向您的存储库添加一些额外的方法:
Specification<Article> spec = Specifications.<Article>where((root, query, cb) -> {
return cb.and(
cb.like(root.get("author"), "Joe%"),
cb.equal(root.get("title"), "Spring boot"));
});
List<Article> articles = repository.findAll(spec);
这允许您动态创建查询,但从您的问题来看,您似乎并不真正需要它。
我最近开始使用Spring引导,并且运行陷入了一点问题。以前,当我只是在休眠和 JPA 中使用 Spring 数据时,我可以创建一个 hibernate.cfg.xml 文件,该文件将提供一堆可以传递给配置对象的配置,然后最终创建一个 SessionFactory 对象这将创建一个会话对象,该对象可用于将查询传递给休眠:
package util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
Configuration configuration = new Configuration().configure("hibernate.cfg.xml"); return configuration.buildSessionFactory( new
StandardServiceRegistryBuilder().applySettings( configuration.getProperties() ).build() );
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() { return sessionFactory; }
}
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hello-world</property>
<property name="connection.username">root</property>
<property name="connection.password">password</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- Create/update tables automatically using mapping metadata -->
<property name="hbm2ddl.auto">update</property>
<!-- Use Annotation-based mapping metadata -->
<mapping class="entity.Author"/>
<mapping class="entity.Article"/>
</session-factory>
</hibernate-configuration>
Main.java
public class HelloWorldClient {
public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction txn = session.getTransaction();
EntityManagerFactory emf = Persiscance.createEntityManagerFactory("hibernate.cfg.xml");
EntityManager em = emf.createEntityManager();
EntityTransaction txn = em.getTransaction();
try {
txn.begin();
Author author = new Author("name", listOfArticlesWritten);
Article article = new Article("Article Title", author);
session.save(author);
session.save(article);
Query query = session.createQuery("select distinct a.authorName from Article s
where s.author like "Joe%" and title = 'Spring boot');
List<Article> articles = query.list();
txn.commit();
} catch(Exception e) {
if(txn != null) { txn.rollback(); }
e.printStackTrace();
} finally {
if(session != null) { session.close(); } }
}
}
这就是问题所在。我不知道如何避免为自定义查询创建 hibernate.cfg.xml 文件或会话工厂。在 Spring 指南页面和我完成的一些教程中,他们采用了他们的 DAO 并扩展了 CrudRepository 接口,该接口已经提供了一堆方法,以及一种命名方法的方法,以便 Hibernate 可以构建sql 独自一人。
我想要完成的,至少在这个 post 是能够在 spring 启动时执行上面的查询。我可以创建一个属性文件
application.properties
# ===============================
# = DATA SOURCE
# ===============================
# Set here configurations for the database connection
spring.datasource.url = jdbc:mysql://localhost:3306/spring-boot-demo
spring.datasource.username = test
spring.datasource.password = test
# Mysql connector
spring.datasource.driverClassName = com.mysql.jdbc.Driver
# ===============================
# = JPA / HIBERNATE
# ===============================
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Ddl auto must be set to "create" to ensure that Hibernate will run the
# import.sql file at application startup
#create-drop| update | validate | none
spring.jpa.hibernate.ddl-auto = update
# SQL dialect for generating optimized queries
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
# ===============================
# = THYMELEAF
# ===============================
spring.thymeleaf.cache = false
#debug=true
我可以将除映射之外的所有内容移动到属性文件,但是我不清楚如何编写查询,因为不再有会话对象。
在 spring 引导应用程序中您无需创建 xml 配置,您必须配置 java 文件本身。 检查这个例子,
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.annotations.common.util.impl.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
@Configuration
public class DatabaseConfig {
private org.jboss.logging.Logger log = LoggerFactory.logger(DatabaseConfig.class);
@Value("${db.driver}")
private String DB_DRIVER;
@Value("${db.username}")
private String DB_USERNAME;
@Value("${db.password}")
private String DB_PASSWORD;
@Value("${db.url}")
private String DB_URL;
@Value("${hibernate.dialect}")
private String HIBERNATE_DIALECT;
@Value("${hibernate.show_sql}")
private String HIBERNATE_SHOW_SQL;
@Value("${hibernate.hbm2ddl.auto}")
private String HIBERNATE_HBM2DDL_AUTO;
@Value("${entitymanager.packagesToScan}")
private String ENTITYMANAGER_PACKAGES_TO_SCAN;
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = null;
try {
dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(DB_DRIVER);
dataSource.setUrl(DB_URL);
dataSource.setUsername(DB_USERNAME);
dataSource.setPassword(DB_PASSWORD);
} catch (Exception e) {
e.getMessage();
}
return dataSource;
}
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan(ENTITYMANAGER_PACKAGES_TO_SCAN);
Properties hibernateProps = new Properties();
hibernateProps.put("hibernate.dialect", HIBERNATE_DIALECT);
hibernateProps.put("hibernate.show_sql", HIBERNATE_SHOW_SQL);
hibernateProps.put("hibernate.hbm2ddl.auto", HIBERNATE_HBM2DDL_AUTO);
sessionFactoryBean.setHibernateProperties(hibernateProps);
return sessionFactoryBean;
}
@Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
}
如果你使用 Spring Boot + Spring Data JPA,然后你配置你的数据源(你现在把它放在 hibernate.cfg.xml)使用 spring.datasource.*
properties.
application.properties
这应该会自动为您创建一个实体管理器。如果需要使用查询,可以使用Spring Data JPA的repositories,例如:
public interface ArticleRepository extends JpaRepository<Article, Long> {
@Query("select s from Article s where s.author like ?1 and s.title = ?2")
List<Article> findByAuthorAndTitle(String author, String title);
}
现在您可以自动装配存储库并使用给定的查询,如下所示:
List<Article> articles = repository.findByAuthorAndTitle("Joe%", "Spring boot");
如果您确实需要自定义查询,可以使用 JPA 中的 Predicate/Criteria API。 Spring 提供了这些谓词的包装版本,称为 Specifications。
为此,您可以使用另一个名为 JpaSpecificationExecutor<Article>
的接口扩展 ArticleRepository
。这会向您的存储库添加一些额外的方法:
Specification<Article> spec = Specifications.<Article>where((root, query, cb) -> {
return cb.and(
cb.like(root.get("author"), "Joe%"),
cb.equal(root.get("title"), "Spring boot"));
});
List<Article> articles = repository.findAll(spec);
这允许您动态创建查询,但从您的问题来看,您似乎并不真正需要它。