JPA non-entity with select database result to List<object[]> - mysql

I want to create a REST API for Android using my yii2 fremowork blocking site and its database.
I did not need to create a table in the database, but I would only SELECT and INSERT the information in the desired style.
How can I do this in the Java Spring boot application?
I need to download the information you need
This is my application.properties file
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/uzaart_teda?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false
My Service implement class
#Service
public class ProductsServiceimpl implements ProductsService{
#Autowired
ProductsRepository productsRepository;
#Override
public List<ProductsDto> getProducts() {
List<ProductsDto> list=new ArrayList<>();
list.add(new ProductsDto(1,2,"anvar",4,5,6,7));
list.add(new ProductsDto(1,2,"sanjar",4,5,6,7));
/*this is my need --->*/
List<Object[]> objects=productsRepository.selectProducts();
/******/
System.out.println(objects.size());
return list;
}
}
This is my Repository
public interface ProductsRepository extends JpaRepository<Object[],Long> {
#Query(value = "SELECT a.id,a.tovar_id,t.nom_sh,a.kol_ost,a.kol_in_ost, a.opt1 AS sot,a.opt1_in AS sot_in FROM s_tovar t,asos_slave a,asos WHERE a.del_flag=1 AND (asos.tur_oper=1 OR asos.tur_oper=4 OR asos.tur_oper=5) AND a.asos_id=asos.id AND a.tovar_id=t.id AND (a.kol_ost>0 OR a.kol_in_ost>0) AND asos.client_id = 4 AND (((t.nom LIKE \"%0001%\") OR (t.nom LIKE \"%0001%\"))) ORDER BY t.nom,a.srok",nativeQuery = true)
public List<Object[]> selectProducts();
}
My result.
Error message
1.
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-07-16 16:20:19.006 ERROR 7328 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'controller': Unsatisfied dependency expressed through field 'productsService';
2.
[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
3.
at com.uz.shopapi.ShopApiApplication.main(ShopApiApplication.java:10) [classes/:na]
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'productsServiceimpl':

This is wrong:
public interface ProductsRepository extends JpaRepository<Object[],Long> {
JpaRepository must be of type of class that is annotated with #Entity:
public interface ProductsRepository extends JpaRepository<Product,Long> {
like this:
#Entity
class Product {
…
}
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.multiple-modules.annotations

You have to create entities to use Spring Data JPA, because it needs the entity metadata to create query.
After that, you can use custom object to select result into it.
public interface ProductsRepository extends JpaRepository<Products,Long> {
#Query(select new com.xyt.CustomObject(a.tovar_id,t.nom_sh ... ) from ENTITY1 a, ENTITIY2 t where ...)
public List<CustomObject> selectProducts();
}
Alternatively, you can use simple JDBC template to create sql query with your custom object mapper. In this way you do not need to create those entities.

Related

Is it possible to use JDBC Template and JDBC MySQL at the same time?

I’m currently building a simple CRUD based app, and I’m done with the basic stuff, like creating a form with a title, date, description etc, editing or deleting a post etc. So now I’m trying to add an image upload function as well. (I’m using Windows 10 as my OS)
At this moment, I’m studying a tutorial of the following URL
https://www.callicoder.com/spring-boot-file-upload-download-jpa-hibernate-mysql-database-example/
, and when I take a look at
Configuring the Database and Multipart File properties
section, it explains what statements are needed to configure the database and multipart file properties, but when I added the tutorial page’s sample it caused conflict.
The below is how my application.properties file looks like.
(The line “## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties” and after that is the part that I copied and pasted from the tutorial, and above that is the original code before adding the tutorial’s)
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.h2.console.enabled=true
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url= jdbc:mysql://localhost:3306/file_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username= root
spring.datasource.password= callicoder
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update
## Hibernate Logging
logging.level.org.hibernate.SQL= DEBUG
## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=200MB
# Max Request Size
spring.servlet.multipart.max-request-size=215MB
The part that’s causing the conflict is the following.
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.url= jdbc:mysql://localhost:3306/file_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username= root
I assume that the reason for the conflict is that I’m trying to use JDBC H2 database and JDBC MySQL at the same time. At first I thought that commenting out my original configuration like below would solve the problem,
#spring.datasource.url=jdbc:h2:mem:test
#spring.datasource.driverClassName=org.h2.Driver
#spring.datasource.username=sa
#spring.h2.console.enabled=true
, but after this I couldn’t run the program probably because there is a part where I use JDBC Templace like below.
[ReportDaoImpl.java]
package com.example.demo.repository;
import java.sql.Timestamp;
#Repository
public class ReportDaoImpl implements ReportDao {
private final JdbcTemplate jdbcTemplate;
#Autowired
public ReportDaoImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Override
public List<Report> findAll() {
String sql = "SELECT report_id, title, threat_level, report_date, description, img_path, "
+ "user.user_id, user_name FROM report "
+ "INNER JOIN user ON report.user_id = user.user_id";
List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql);
……
My biggest question is, how can I integrate the tutorial’s “upload an image” function to my basic CRUD app without causing a conflict in configuration?
Should I give up using JDBC H2 Database and JDBC Template and use something else that is compatible with the JDBC MySQL part that I’m taking from the tutorial? In other words, in order to integrate the tutorial’s image upload function, should I fundamentally restructure my code in ReportDaoImpl.java file (and maybe even other files as well?), or would there be a simple way to resolve the configuration conflict?
You don't have to give away any of your Databases if you intend to use them for absolutely necessary reasons. Spring doesn't DataSourceAutoConfiguration can't differentiate between the two configs in the property file for the simple fact that the property file is a key-value pair map. And hence it would override the configs.
The easiest way to resolve this is:
Create separate keys for different datasources as under:
## Your Primary Data Source
spring.datasource-primary.url=jdbc:h2:mem:test
spring.datasource-primary.driverClassName=org.h2.Driver
spring.datasource-primary.username=sa
spring.h2.console.enabled=true
## Your Secondary Data Source
spring.datasource-secondary.url= jdbc:mysql://localhost:3306/file_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource-secondary.username= root
spring.datasource-secondary.password= callicoder
Add a DataSourceConfig as under
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
/**
* Configures the Spring-managed resources for Common Services/Utils.
*/
#Configuration
public class DataSourceConfig {
#Autowired
Environment env;
/**
* Primary DataSource (Meaning the one that is your parent transaction manager)
*/
#Bean
#Primary
public DataSource h2DataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource-primary.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource-primary.url"));
dataSource.setUsername(env.getProperty("spring.datasource-primary.username"));
dataSource.setPassword(env.getProperty("spring.datasource-primary.password"));
return dataSource;
}
/**
* #usage Autowire this in your JPA Repositories using
* #Autowired
* JdbcTemplate h2JdbcTemplate;
*/
#Bean
public JdbcTemplate h2JdbcTemplate() {
return new JdbcTemplate(h2DataSource());
}
/**
* Secondary DataSource (Meaning the one that can cause the parent transaction to roll-back on exception)
*/
#Bean
public DataSource mysqlDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource-secondary.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource-secondary.url"));
dataSource.setUsername(env.getProperty("spring.datasource-primary.username"));
dataSource.setPassword(env.getProperty("spring.datasource-secondary.password"));
return dataSource;
}
/**
* #usage Autowire this in your JPA Repositories using
* #Autowired
* JdbcTemplate mysqlJdbcTemplate;
*/
#Bean
public JdbcTemplate mysqlJdbcTemplate() {
return new JdbcTemplate(mysqlDataSource());
}
}
Use in right JdbcTemplate in your repository classes
package com.example.demo.repository;
import java.sql.Timestamp;
#Repository
public class ReportDaoImpl implements ReportDao {
//Note the JdbcTemplate variable name here
private final JdbcTemplate myslJdbcTemplate;
#Autowired
//Note the JdbcTemplate variable name here
public ReportDaoImpl(JdbcTemplate myslJdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Override
public List<Report> findAll() {
String sql = "SELECT report_id, title, threat_level, report_date, description, img_path, "
+ "user.user_id, user_name FROM report "
+ "INNER JOIN user ON report.user_id = user.user_id";
List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql);
……
You would need to update the repective JdbcTemplate for all the respective repository classes.
Cheers and happy coding!
You cannot define the same key multiple times in application.properties , one will override the other. That means if you need to use multiple datasources (for MySQL and H2), you cannot relies on the spring.datasource.xxx in application.properties. Instead, define the two DataSource explicitly by yourself. See the official docs for an example.
Also , JdbcTemplate will only be configured if :
Only one DataSource defined
If multiple DataSource are defined but only one DataSource is marked as #Primary, and it will configured just for this #Primary DataSource.
So that means after you define multiple DataSource , you have to mark the H2 one as #Primary such that the JdbcTemplate will be auto configured for it only to ensure that your existing JDBCTempalte related codes still interact with H2 but not MySQL.
By the way, there is no advantages to use multiple database for a simple CRUD app. You will encounter issues if you want to have a transaction which cover the data from multiple databases. I suggest you only choose one for such a simple app.
(Also see my related answer for more detail)

How to access java abc.properties file values in json formate using Spring framework

i'm trying to access the values of properties file in spring framework. now i have bean file and controller. so how to access properties file value in json formate using bean
For accessing single value can be used Spring annotations "PropertySource" and "Value".
#PropertySource("classpath:application.properties")
public class SomeClass {
#Value("${some.property}")
private String someProperty;
...
}
For accessing/looping all spring properties, check this solution looping-through-all-the-properties-in-a-file-with-spring-and-java
Controller sample code:
#RestController
public class PropertiesController {
#Autowired
Properties props;
#RequestMapping(value = {"/properties"}, method = RequestMethod.GET, produces= MediaType.APPLICATION_JSON_UTF8_VALUE)
public Set<Map.Entry<Object, Object>> getProperties() {
return props.entrySet();
}
}
if you are Using spring-boot then add spring-actuator dependency which by defaults expose /env endpoint and spits out all the properties loaded in the spring container in json format.

Spring + MyBatis - setting Data Source

I'm integrating MyBatis inside my SpringBoot application. The application connects to a MySql database to fetch data. Right now I have the following classes.
MyBatisUtils.java
[...]
#Component
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(getConfiguration());
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
private static Configuration getConfiguration(){
Configuration configuration = new Configuration();
DataSource dataSource = null; //wrong!!!
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
configuration.addMapper(BaseQuery.class);
return configuration;
}
}
Search.java
[...]
public List dynamicSearch(){
SqlSession session = MyBatisUtils.getSqlSessionFactory().openSession();
BaseQuery mapper = session.getMapper(BaseQuery.class);
List<HashMap<String, Object>> result = mapper.select(/*query parameters*/);
return result;
}
I do not know how to set my DataSource object inside the MyBatisUtils class. Should it have some connection parameters?
Thanks for the help.
Define the DataSource as a Spring bean, like in this other question:
How to Define a MySql datasource bean via XML in Spring
Then inject the datasource in MyBatisUtils class.
You can also define SqlSessionFactory as a Spring bean, and directly inject it. Useful reference: http://www.mybatis.org/spring/getting-started.html
If you are using spring-boot already you can use mybatis-spring-boot-starter and auto-configure mybatis for free. The only thing you should worry about is the datasource. For that, properties should be set in application.properties
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
You can find more info here

Jackson tries to cast Integer to String

I have a very basic Spring Boot app with .-data-jpa, .-data-rest and .-web dependencies.
In my model there is an entity Game that contains an Integer property homeGameSwitch.
When I get the resource with a REST call I get this exception:
.w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP
message:
org.springframework.http.converter.HttpMessageNotWritableException:
Could not write JSON: java.lang.Integer cannot be cast to
java.lang.String; nested exception is
com.fasterxml.jackson.databind.JsonMappingException: java.lang.Integer
cannot be cast to java.lang.String (through reference chain:
org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer$1["content"]->com.coli.stripebackend.model.Game["homeGameSwitch"])
I find it strange that Jackson can't handle an Integer.
Is there something I can do prevent this error?
The entity:
#Entity
public class Game {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private Integer homeGameSwitch;
public Integer getHomeGameSwitch() {
return homeGameSwitch;
}
The DAO:
#Repository("gameDao")
public interface GameDao extends JpaRepository<Game, Integer> {}
The service:
#Service("gameService")
public class GameServiceImpl implements GameService {
#Autowired
private GameDao gameDao;
#Override
public Game retrieveGameById(Integer id) throws Exception {
Optional<Game> optionalGame = gameDao.findById(id);
return optionalGame.get();
}
The error occurs when calling localhost:8080/game/7
The problem was related to the version of Spring Boot. I was using the version 2.0.0.M2 in which there seems to be a bug.
I don't have the problem when using the version 1.5.4.RELEASE.
This is a warning to future readers who come here: There are many ways to trigger this exception: Some similar ones are collected in Jackson serialization exception when trying to serialize LocalDateTime
It seems I just ran into another way of getting the exact same exception: Have a class like this:
class Foo extends HashMap<String, String> {
public long bar;
}
and try to serialize it.

LazyInitializationException trying to get lazy initialized instance

I see the following exception message in my IDE when I try to get lazy initialized entity (I can't find where it is stored in the proxy entity so I can't provide the whole stack trace for this exception):
Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate com.epam.spring.core.domain.UserAccount_$$_jvste6b_4.toString()
Here is a stack trace I get right after I try to access a field of the lazy initialized entity I want to use:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at com.epam.spring.core.domain.UserAccount_$$_jvstfc9_4.getMoney(UserAccount_$$_jvstfc9_4.java)
at com.epam.spring.core.web.rest.controller.BookingController.refill(BookingController.java:128)
I'm using Spring Data, configured JpaTransactionManager, database is MySql, ORM provider is Hibernate 4. Annotation #EnableTransactionManagement is on, #Transactional was put everywhere I could imagine but nothing works.
Here is a relation:
#Entity
public class User extends DomainObject implements Serializable {
..
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "user_fk")
private UserAccount userAccount;
..
#Entity
public class UserAccount extends DomainObject {
..
#OneToOne(mappedBy = "userAccount")
private User user;
..
.. a piece of configuration:
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROP_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROP_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROP_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROP_NAME_DATABASE_PASSWORD));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setJpaProperties(getHibernateProperties());
return entityManagerFactoryBean;
}
#Bean
public JpaTransactionManager transactionManager(#Autowired DataSource dataSource,
#Autowired EntityManagerFactory entityManagerFactory) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
jpaTransactionManager.setDataSource(dataSource);
return jpaTransactionManager;
}
.. and this is how I want to retrieve UserAccount:
#RequestMapping(...)
#Transactional()
public void refill(#RequestParam Long userId, #RequestParam Long amount) {
User user = userService.getById(userId);
UserAccount userAccount = user.getUserAccount();
userAccount.setMoney(userAccount.getMoney() + amount);
}
Hibernate version is 4.3.8.Final, Spring Data 1.3.4.RELEASE and MySql connector 5.1.29.
Please, ask me if something else is needed. Thank you in advance!
Firstly, you should understand that the root of the problem is not a transaction. We have a transaction and a persistent context (session). With #Transactional annotation Spring creates a transaction and opens persistent context. After method is invoked a persistent context becomes closed.
When you call a user.getUserAccount() you have a proxy class that wraps UserAccount (if you don't load UserAccount with User). So when a persistent context is closed, you have a LazyInitializationException during call of any method of UserAccount, for example user.getUserAccount().toString().
#Transactional working only on the userService level, in your case. To get #Transactional work, it is not enough to put the #Transactional annotation on a method. You need to get an object of a class with the method from a Spring Context. So to update money you can use another service method, for example updateMoney(userId, amount).
If you want to use #Transactional on the controller method you need to get a controller from the Spring Context. And Spring should understand, that it should wrap every #Transactional method with a special method to open and close a persistent context. Other way is to use Session Per Request Anti pattern. You will need to add a special HTTP filter.
https://vladmihalcea.com/the-open-session-in-view-anti-pattern/
As #v.ladynev briefly explained, your issue was that you wanted to initialize a lazy relation outside of the persistence context.
I wrote an article about this, you might find it helpful: https://arnoldgalovics.com/lazyinitializationexception-demystified/
For quick solutions despite of performance issues use #transactional in your service
Sample:
#Transactional
public TPage<ProjectDto> getAllPageable(Pageable pageable) {
Page<Project> data = projectRepository.findAll(pageable);
TPage<ProjectDto> response = new TPage<>();
response.setStat(data, Arrays.asList(modelMapper.map(data.getContent(), ProjectDto[].class)));
return response;
}
it will get user details for project manager in the second query.
For more advanced solution, you should read the blog post in the #galovics answer.
I used below to fix
sessionFactory.getObject().getCurrentSession()
Create query and get required object
I was also facing the same error while running my springBoot App.
What is the real issue here?
Please check have you autowired the repository at controller level
If first step is correct then please check where ever you have autowired your JPA repository , it should be a part of #Transactional code.
If not please add #Transactional annotation.It will solve your issue.
I was getting this error:
Method threw 'org.hibernate.LazyInitializationException' exception.
This is because currently there is no session present. Hibernate opens a session and closes it, but for "lazy = true" or "fetch = FetchType.LAZY" such fields are populated by proxies. When you try to find the value for such a field, it will attempt to go to the database using the active session to retrieve the data. If no such session can be found, you get this exception.
You can fix it using "lazy=false" or check whether you have used #Transcational properly (try to use this in your service layer than your data access layer), you can also use
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
OR
#Transactional