In my project we are caching the data of a table where we use #NamedQuery for fetching the data (Hibernate queries) and we are also using #NamedNativeQueries and my code is like that
#SpringBootTest
public class NonPolygonIntegeration {
#Autowired
private Repository localRepository;
#MockBean
ServicePropertiesRepository servicePropertiesRepository;
#MockBean
CacheService alendarCacheService;
#Autowired
OlygonService olygonService;
#Test
#Order(1)
#Sql(scripts = {"/sqls/mdm.sql", "/sqls/p.sql","/sqls/q.sql",
"/sqls/x.sql",
"/sqls/y.sql",
"/sqls/z.sql"
}, config = #SqlConfig(encoding = "utf-8"))
void testGetAllActiveCurrencies_forDataPresentInDb() {
}
}
"""
I have data in my local database for all the corresponding tables in use.
query which will get in use are
#Query("Select * FROM Properties WHERE id.Code =:serviceCode AND (endDate IS NULL OR endDate >= CURRENT_DATE)")
and other is
select * from empoyee_table where employee_id = :id
How to handle such scenario?
Related
I am trying to test this method:
#Query(value = "select * from table "
+ "where match(name_column) AGAINST(:name in BOOLEAN MODE)", nativeQuery = true)
List<ManagedGroup> findByGroupNameMatches(#Param("name") String groupName);
The test itself :
#Test
public void findByGroupNameMatches_should_return_when_contains_part_of_name(){
ManagedGroup managedGroup = prepearGroup(UUID.randomUUID().toString());
managedGroup.setGroupName("def123");
testEntityManager.persistAndFlush(managedGroup);
// in desperate tries - tried to save it via repositroty as well
groupRepository.save(managedGroup);
//the groups can be found - there is two groups with names 'def123' and 'default name'
List<ManagedGroup> res = groupRepository.findAll();
Pageable pageable = new OffsetLimitPageRequest(0, 10, Sort.by(Sort.Direction.DESC, "id"));
List<ManagedGroup> managedGroupPage = groupRepository.findByGroupNameMatches("+def*");
//managedGroupPage is empty after the method is called
Assertions.assertEquals(defaultManagedGroup, managedGroupPage.get(0));
}
The test fails cause I get empty result from findByGroupNameMatches. I've tried saving and retriviengthe entity via jdbcTemplate - still no result. Other tests work as well, so I guess that it is not the problem.
The method works in implemented controller and is already tested by QA team.
I've also tried to run the test against QA db and it works with the entities, persisted before tests are booted. I am sure that the problem is with persisting it, but I still cannot find the solution. I've tried different combinations of flush and etc.. and still no result
Providing more info to reproduce the test:
static final MySQLContainer DATABASE = new MySQLContainer("mysql:8.0.23");
static {
DATABASE.start();
}
static class Initializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext context) {
TestPropertyValues.of(
"spring.datasource.url=" + DATABASE.getJdbcUrl(),
"spring.datasource.username=" + DATABASE.getUsername(),
"spring.datasource.password=" + DATABASE.getPassword()
).applyTo(context.getEnvironment());
}
The test class is annotated with
#DataJpaTest
#ExtendWith(SpringExtension.class)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#DisabledIfEnvironmentVariable(named = "RUN_INTEGRATION_TEST", matches = "false")
#DirtiesContext
}
Hello shouldnt you have a table name for this native query
#Query(value = "select * from table TABLE_NAME "
+ "where match(name_column) AGAINST(:name in BOOLEAN MODE)", nativeQuery = true)
List<ManagedGroup> findByGroupNameMatches(#Param("name") String groupName);
Also you should flush when Transactional annotation not there
// in desperate tries - tried to save it via repositroty as well
groupRepository.save(managedGroup);
use groupRepository.flush or saveAndFlush
Is there a possibility to run some custom SQL query generated via 3rd party tools like e.g. jOOQ and still benefit from Spring Data JDBC mapping features i.e. #Column annotations? I don't want to use #Query annotation.
class Model { #Id private Long id; #Column("column") private String prop; }
class MyRepo {
public Model runQuery() {
val queryString = lib.generateSQL()
// run query as if it has been generated by Spring Data JDBC and map
// results to Model automatically
}
}
Perhaps JdbcTemplate can solve your problem? Specifically, one of the queryForObject() methods may be of interest since your are requesting a single object:
class MyRepo {
private JdbcTemplate jdbcTemplate;
#Autowired
MyRepo(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Model runQuery() {
val query = lib.generateQuery();
return jdbcTemplate.queryForObject(query, Model.class);
}
}
More information and other use cases can be found in the Spring Guide Accessing Relational Data using JDBC with Spring
I have a simple rest API using spring boot 2 and Java 11, and I have some integration tests. Some of the tests insert into the DB and assert that the data is read from the API, and some create objects via the API and assert they can be read back from the DB or the API.
The latter tests, which create objects via the API, seem to not be reseting the auto-increment value in the test h2 database. The symptom is that the tests pass independently, but not when run as a group - the IDs are the sum of all inserts so far.
I have an #Before method that drops and recreates every table, and an #After method that drops every table, and I've verified that they are running.
There are lots of questions about this but none of the suggested answers make any difference. I have tried:
Decorating the test class with #DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
Decorating the test methods with #Transactional
Running ALTER TABLE person ALTER COLUMN id RESTART WITH 1; repeatedly, including after the table creation and again right before each object creation
My code looks like this:
IntegrationTest.java
#EnableConfigurationProperties
#RunWith(SpringJUnit4ClassRunner.class)
#PropertySource(value = "classpath:application.properties")
#SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public abstract class IntegrationTest {
#Autowired
private DataSource dataSource;
#Autowired
JdbcTemplate jdbcTemplate;
RestTemplate restTemplate = new RestTemplate();
#Before
public void SetUp() throws SQLException {
ScriptUtils.executeSqlScript(dataSource.getConnection(), new FileSystemResource("src/test/resources/initialization.sql"));
}
#After
public void CleanUp() throws SQLException {
ScriptUtils.executeSqlScript(dataSource.getConnection(), new FileSystemResource("src/test/resources/cleanup.sql"));
}
}
PersonTest.java
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class PersonTest extends IntegrationTest {
#Test
#Transactional
public void TestCreatePerson() {
String url = "http://localhost:8080/people";
Person p = new Person();
p.setFirstName("Joe");
p.setLastName("Smith");
Person r = restTemplate.postForObject(url, p, Person.class);
Assert.assertEquals(1, (long) r.getId());
}
#Test
#Transactional
public void TestUpdatePerson() {
jdbcTemplate.execute("alter table person alter column id restart with 1");
String url = "http://localhost:8080/people";
Person p = new Person();
p.setFirstName("Joe");
p.setLastName("Smith");
Person r = restTemplate.postForObject(url, p, Person.class);
Assert.assertEquals(1, (long) r.getId()); // FAILS HERE BECAUSE ID == 2
r.setLastName("Jones");
restTemplate.put(url, r);
url = "http://localhost:8080/people/1";
Person u = restTemplate.getForObject(url, Person.class);
Assert.assertEquals("Jones", u.getLastName());
}
initialization.sql
DROP TABLE IF EXISTS person;
CREATE TABLE IF NOT EXISTS person (
id int(11) NOT NULL AUTO_INCREMENT,
first_name varchar(128) NOT NULL,
last_name varchar(128) NOT NULL
);
-- for good measure
ALTER TABLE person ALTER COLUMN id RESTART WITH 1;
cleanup.sql
DROP TABLE IF EXISTS person;
Any ideas what I am doing wrong?
Normally I use annotiations:#Query("SELECT c FROM Country c") with JpaRepositoryor predefined methods like findAll
but in my case I want to generate dynamic query.
String baseQuery =SELECT c FROM Country c`
if(age!=null)
baseQuery+="WHERE c.age=20"
I need to perform same query from code level like this:
Query q1 = em.createQuery("SELECT c FROM Country c");
but I dont use EntityManager in spring boot
How can I generate query from code level?
If you would like to create dynamic queries from code you can take advantage of Spring's JdbcTemplate. Using spring boot it is as simple as injecting JdbcOperations bean to your repository class (assuming you have provided spring-boot-starter-jdbc module to your project).
But remember! This solution uses SQL, not JPQL. That's why you have to use proper tables and columns names in queries and properly map result to objects (i.e. using RowMapper)
This simple example worked fine for me (with different entity, but in same manner - I've adapted it to your example):
#Repository
public class CountryRepository {
#Autowired
private JdbcOperations jdbcOperations;
private static String BASIC_QUERY = "SELECT * FROM COUNTRY";
public List<Country> selectCoutry(Long age){
String query = BASIC_QUERY;
if (age != null){
query += " WHERE AGE = ";
query += age.toString();
}
//let's pretend that Country has constructor Conutry(String name, int age)
return jdbcOperations.query(query, (rs, rowNum) ->
{ return new Country(rs.getString("NAME"), rs.getInt("AGE");}
);
};
}
Then in service or whatever you inject CountryRepository and call method.
Since you're using Spring Boot, you can use Spring Data to create queries in your repository:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
}
Not a 100% on syntax, but should be something similar.
Now you can autowire this class:
#Autowired
public CountryRepository countryRepo;
And all basic methods are already available to you like:
countryRepo.findOne(id);
countryRepo.find();
If you want to make more advanced queries, you can use Spring Data e.g.:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
public Country findByNameAndContinent(String name, String continent);
}
This is just an example (a stupid one) of course and assumes your Country class has the field names 'name' and 'continent' and both are strings. More is available here:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Section 5.3 more specifically.
PS: Make sure your Country class has the #Entity annotation
I am new to JPA and Spring Boot and trying to write a custom query which returns only one column from the table when the API is triggered. But I get an error doing so. It would be helpful if someone could guide me to a correct way of writing this custom query.
// Controller class
#Autowired
private UserTestInstanceDao usertestdao;
List<String> usertestinst = usertestdao.tempQuery();
// DAO class
public interface UserTestInstanceDao extends CrudRepository<UserTestInstance, Long> {
#Query("SELECT ti.test_name FROM test_instance ti")
public List<String> tempQuery();
}
I think that your query should look like this (if you follow conventions):
#Query("SELECT ti.testName FROM UserTestInstance ti")
For this query, you UserTestInstance should look like this:
public class UserTestInstance {
private String testName;
<getters and setters>
}
This is because you're using JPQL, and you should be querying your objects and their declared variables. It's Spring's Data job to translate to your database specific db query.
Spring Data documentation for reference.
It's ok but you have two options :
You must put your name class in the from clause #Query("SELECT ti.testName FROM UserTestInstance ti")
Use a native query whith the real name of Database #Query(value="SELECT ti.test_name FROM test_instance ti",nativeQuery=true)
I used JpaRepository for this purpose.
Controller class
#Autowired
private UserTestInstanceDao usertestdao;
//in method
List<String> usertestinst = usertestdao.tempQuery();
DAO class:
public interface UserTestInstanceDao extends JpaRepository<UserTestInstance, Long> {
#Query(value="SELECT test_name FROM ti", nativeQuery = true)
public List<String> tempQuery();
}
And these are in my application.properties:
# DATASOURCE
spring.datasource.url=jdbc:mysql://localhost/test_instance
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# JPA
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.data.jpa.repositories.enabled=true
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect