What I am trying to perform is the following:
I have some complex SQL (with SUM(distance) distanceSum as identifier for the returned columnd) that returns some values that should be parsed to a class (containing just the values needed for these columns).
However, I only need the result in memory and not as entity.
I already tried to create a repository to execute the SQL with a #Query annotation with native = true. However, the repository can't be autowired, probably because Repositories are only meant for entities.
So is there some way to tweak a repository for non-entities or is there a approach other than repositories that would let me execute SQL and parse the result automatically into an object.
Basically as #dunni said you can use the JdbcTemplate with your own mapper to convert the SQL result to Java POJO:
public CustomResult getCustomResult(){
final String complexSql = "SELECT SUM(distance) as distanceSum....";
final CustomResult customResult = (CustomResult) jdbcTemplate.queryForObject(complexSql, new CustomResultRowMapper());
return customResult;
}
public class CustomResultRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
CustomResult customResult = new CustomResult();
customResult.setDistanceSum(rs.getInt("distanceSum"));
...
return customResult;
}
}
Also in Spring Boot you don't need to do anything just add your jdbcTemplate to your Dao class:
#Autowired
private JdbcTemplate jdbcTemplate;
Related
I have a native query to fetch a sequence of the form:
#Repository
public class GetSequenceRepository {
#PersistenceContext
private EntityManager entityManager;
public String getSequenceUsingNativeQuery() {
// POSTGRES Syntax
return entityManager.createNativeQuery("SELECT nextval ('my_custom_seq')")
.getSingleResult().toString();
}
}
Since the syntax is different for Postgres, MySQL and Oracle; I want to create a query to get sequence value in a database-agnostic manner.
I want something like #Sequencegenerator but at the Repository layer. Is there implementation of #Sequencegenerator at Repository layer.
Note:
I have a sequence already present in Database.
You can use the following:
entityManager.getEntityManagerFactory()
.unwrap(SessionFactoryImplementor.class)
.getIdentifierGenerator("your.hibernate.entity.name")
.generate(entityManager.unwrap(SharedSessionContractImplementor.class), null)
If the sequence is not mapped for an entity, you need to construct a IdentifierGenerator yourself:
IdentifierGeneratorFactory identifierGeneratorFactory = new DefaultIdentifierGeneratorFactory();
identifierGeneratorFactory.setDialect(entityManager.getEntityManagerFactory().unwrap(SessionFactoryImplementor.class).getDialect());
Properties properties = new Properties();
properties.put("sequence_name", "YOUR_NAME");
IdentifierGenerator identifierGenerator = identifierGeneratorFactory.createIdentifierGenerator("sequence", StringType.INSTANCE, properties);
identifierGenerator.generate(entityManager.unwrap(SharedSessionContractImplementor.class), null);
I am writing JPA implementation to replace JDBC implementation with query. I have used the Oracle database sequence object name in #SequenceGenerator as shown in the code. As a result, save() is returning an already existing data in the table instead of generating a new primary key and inserting into the table.
I think the sequence is generating existing primary key instead of generating a new one.
#Entity
#Table(name = "table")
public class TableDetail implements java.io.Serializable{
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "table_seq")
#SequenceGenerator(sequenceName = "SEQ_TABLE", allocationSize = 1, name = "table_seq")
private Long AUDT_ID;
....
}
#Repository
public interface TableDetailDAO extends CrudRepository<TableDetail, Long> {
TableDetail save(TableDetail tableDetail);
}
#Service
#Transactional
public class TableDetailServiceImpl implements TableDetailService {
public void createAuditEvent(TableDetail tableDetail) {
#Autowired
TableDetailDAO tableDetailDAO;
TableDetail tableDetail =
tableDetailDAO.save(tableDetail);
}
}
So I figured out that it was due to my JDBC Database config file where I had defined this TransactionManager as shown. I had to remove this or define a JpaTransactionManager in this method. Right now, this definition was not letting JPA handle the commit due to which the data was not being persisted into the database.
This definition was present because it was actually a JDBC implementation before.
Also, I had to use GenerationType.AUTO because as far as my understanding, GenerationType.SEQUENCE is used only when there is no Sequence object defined in the Database.
/**
* Creates a handle to the TransactionManager.
*
* #return PlatformTransactionManager
*/
#Bean(name = "transactionManager")
public PlatformTransactionManager txManager() {
return new DataSourceTransactionManager(initializeDataSource());
}
I am able to get the property value in Spring classes like below:
#Value("${database.name}")
private String databaseName;
I have to execute a native query by joining different tables which are in different databases.
#Query(value="select t1.* FROM db1.table1 t1 INNER JOIN db2.table2 t2 ON t2.t1_id1 = t1.id1")
Instead of hard coding database names i.e., db1 and db2 here, I have to get them from properties file.
how to get the property value inside the #Query annotation in Spring Data JPA Repository ?
I don't know if it is possible, but if not, you can consider this approach:
Instead of using properties in Repository's #Query directly, you can use params in the query but when you call the actual method - you can provide values from .properties.
Imagine you have simple repository:
public interface UserRepository extends JpaRepository<User, Long> {
// query with param
#Query("select u from User u where u.lastname = :lastname")
User findByLastname(#Param("lastname") String lastname);
}
Then, let's say you have some Service or Controller where you need to use your Repository - you can inject properties there and pass them to your method:
#Service
public class UserService {
// this comes from .properties
#Value("${user.lastName}")
private String userLastName;
#Autowired
private UserRepository userRepository;
public User getUser() {
// you pass it as param to the repo method which
// injects it into query
return userRepository.findByLastname(userLastName);
}
}
This is just an example. But I believe it may be useful.
Happy hacking :)
I have been working to generalize the methods of the DAO for a project using Spring, JPA and Hibernate. However, I am still very much learning Spring, Java, and coding in general.
Is the below design bad or perfectly fine? Is there a better way to accomplish the same thing? Any advice would be greatly appreciated.
I have simplified the class:
#Repository
public class TestRepository
{
#PersistenceContext
private EntityManager entityManager;
public List<?> getListResults(Class<?> dtoClass, String sqlString)
{
List<?> returnList = null;
Query query = entityManager.createNativeQuery(sqlString, dtoClass);
try
{
returnList = (List<?>) query.getResultList();
}
catch (Exception e)
{
}
return returnList;
}
}
Spring Data JPA is the must convenient way in order to interact with your databases because it helps you to avoid the common mistakes that occurs when you try to configure your ORM mapping, entityManager, transacctionManager and all the rest of necessary components in order to establish a communication between your entity domains and your database.
For example you have a pojo like this:
#Entity
public class Item {
#Id
private Long id;
......
}
You can create an interface in order to get or put information to the item repository like this:
public interface ItemRepository extends from JpaRepository<Item,Long>{}
When you need to save the Item just #Autowired the ItemRepository, this is the must important part because the previous interface that is created without methods now exposes ready-to-work methods that will interact with your database, this is the abstraction level that makes Spring Data JPA very useful:
#Autowired
ItemRepository itemRepo
public void createItem(){
Item item = new Item();
itemRepo.save(item);
//or you can get information
List<Item> itemList = itemRepo.findAll();
}
More information in Spring Data JPA Documentation
How about using Spring Data Repositories?
#Repository
public interface SomethingRepository extends JpaRepository<Something, Long> {
}
That way you get lots of methods without having to manually write your SQL query as a string, you retain type safety and you can leverage the power of JPA queries and dynamic proxies that do this whole SQL business for you.
I need to write some temporary code in my existing Spring Boot 1.2.5 application that will do some complex SQL queries. By complex, I mean a single queries about 4 different tables and I have a number of these. We all decided to use existing SQL to reduce potential risk of getting the new queries wrong, which in this case is a good way to go.
My application uses JPA / Hibernate and maps some entities to tables. From my research it seems like I would have to do a lot of entity mapping.
I tried writing a class that would just get the Hibernate session object and execute a native query but when it tried to configure the session factory it threw an exception complaining it could not find the config file.
Could I perhaps do this from one of my existing entities, or at least find a way to get the Hibernate session that already exists?
UPDATE:
Here is the exception, which makes perfect sense since there is no config file to find. Its app configured in the properties file.
org.hibernate.HibernateException: /hibernate.cfg.xml not found
at org.hibernate.internal.util.ConfigHelper.getResourceAsStream(ConfigHelper.java:173)
For what it's worth, the code:
#NamedNativeQuery(name = "verifyEa", query = "select account_nm from per_person where account_nm = :accountName")
public class VerifyEaResult
{
private SessionFactory sessionFact = null;
String accountName;
private void initSessionFactory()
{
Configuration config = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).getBootstrapServiceRegistry();
sessionFact = config.buildSessionFactory(serviceRegistry);
}
public String getAccountName()
{
// Quick simple test query
String sql = "SELECT * FROM PER_ACCOUNT WHERE ACCOUNT_NM = 'lynnedelete'";
initSessionFactory();
Session session = sessionFact.getCurrentSession();
SQLQuery q = session.createSQLQuery(sql);
List<Object> result = q.list();
return accountName;
}
}
You can use Data access with JDBC, for example:
public class Client {
private final JdbcTemplate jdbcTemplate;
// Quick simple test query
final static String SQL = "SELECT * FROM PER_ACCOUNT WHERE ACCOUNT_NM = ?";
#Autowired
public Client(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
public List<Map<String, Object>> getData(String name) {
return jdbcTemplate.queryForList(SQL, name);
}
}
The short way is:
jdbcTemplate.queryForList("SELECT 1", Collections.emptyMap());