how to execute sql statements inside spring boot controller? - java

I want to execute sql statement inside my spring boot controller class with out defining any method in the jpa repository. The statement i want to use is
SELECT UUID();
This statement is database related and is not associated with a particular entity.
It would be nice if any one can provide solution for the execution of the above statement via
spring controller class
jpa repository (if recommended)
update
controller:
#Autowired
JdbcTemplate jdbcTemplate;
#RequestMapping(value = "/UUID", method = RequestMethod.GET)
public ResponseEntity<String> getUUID() {
String uuid = getUUID();
return buildGuestResponse(uuid);
}
public String getUUID(){
UUID uuid = (UUID)jdbcTemplate.queryForObject("select UUID()", UUID.class);
return uuid.toString();
}

You can use JdbcTemplate in your code.
The bean you will need in your configuration class is:-
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource)
{
return new JdbcTemplate(dataSource);
}
And the code to run the query is:-
#Autowired
private JdbcTemplate JdbcTemplate;
public String getUUID(){
UUID uuid = (UUID)jdbcTemplate.queryForObject("select UUID()", UUID.class);
return uuid.toString();
}
or may be like this:-
public UUID getUUID(){
UUID uuid = (UUID)jdbcTemplate.queryForObject("select UUID()", UUID.class);
return uuid;
}

This is generally architecturally bad design to execute any SQL (do any persistence) on presentation layer (controller or view) in JEE applications.
The best option is make controller to use service layer, when service layer calling the persistence layer for: obtaining, saving or updating data.
In any case, you can use Spring Data JDBC. Something like:
import org.springframework.jdbc.core.JdbcTemplate;
....
UUID uuid = (UUID)jdbcTemplate.query("SELECT UUID()", UUID.class);
....

Related

How to mock dao layer with the same methods in service layer?

I am kinda confuse how to test my dao layer which contain same method as my service layer. I managed to test my service layer and stuck figuring out how to test my dao layer.
#Repository
public class DaoImpl implements Dao {
#Autowired
private NamedParameterJdbcTemplate template;
#Override
public Set<MyForm> findSomething(String title, String name, ZonedDateTime datetime) {
String sql= "SELECT * from myTable WHERE title= :d_title AND name = :d_name AND mydatetime= :d_datetime";
SqlParameterSource namedParameters = new MapSqlParameterSource()
.addValue("d_title", title)
.addValue("d_name",name)
.addValue("d_datetime",datetime.withZoneSameInstant(ZoneId.of("UTC")).toOffsetDateTime());
List<MyForm> myForm= template.query(sql, namedParameters, new MyRowMapper());
Set<MyForm> setMyForm = new HashSet<MyForm>(myForm);
return setMyForm ;
}
To test your datalayer you can make use of Spring Boots Testslice DataJdbcTest.
#DataJdbcTest
public class DaoImplIT {
#Autowired
private DaoImpl daoImpl;
#Test
void test() {
// prepare your database
// call the method to test
// make assertions about the result
}
}
In this testslice Spring Boot will configure all beans related to data access via JDBC. If you provide an in-memory DB (e.g. H2), it will be used automatically.
The main idea of this type of test is to prepare your database by inserting data and then use your repository beans to query them.
I wrote a blog post about this (okay, it's #DataJpaTest but the main idea is the same.). Maybe it will help you.

Create new PostgresSQL schema

What's the easiest way to create a new Postgres scheme inside the database on the runtime and also, create the tables written inside a SQL file?
\This is a Spring boot application and the method receives the schema name that needs to be created for the db.
Although it sounds like this would be a case for using Liquibase or Flyway or any other tool, here is a simple (but very hacky) solution/starting point:
(rough) Steps:
create the whole ddl query, which consists of the "create and use schema part" and the content of your SQL file
inject the entity manager
run the whole ddl query as a native query
Example/(hacky) Code:
Here a simple controller class defining a GET method that takes a parameter called "schema":
#Controller
public class FooController {
private static final String SCHEMA_FORMAT = "create schema %s; set schema %s; ";
#PersistenceContext
EntityManager entityManager;
#Value("classpath:foo.sql")
Resource fooResource;
#GetMapping("foo")
#Transactional
public ResponseEntity<?> foo(#RequestParam("schema") String schema)
throws IOException {
File fooFile = new ClassPathResource("foo.sql").getFile();
String ddl = new String(Files.readAllBytes(fooFile.toPath()));
String schemaQuery = String.format(SCHEMA_FORMAT, schema, schema);
String query = String.format("%s %s", schemaQuery, ddl);
entityManager.createNativeQuery(query).executeUpdate();
return ResponseEntity.noContent().build();
}
}

How to test spring batch job within #Transactional SpringBootTest test case?

I just can't seem to win today...
Is there a way to read from a OneToMany relationship in a Spock SpringBootTest integration test, without annotating the test as #Transactional or adding the unrealistic spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true?
OR, is there a way to launch a Spring-Batch Job from within a #Transactional test case?
Let me elaborate...
I'm trying to get a simple Spring Boot Integration test working for my Spring Batch reporting process, which reads from tangled web of DB2 tables and generates a series of change messages for interested systems. I'm using the Groovy Spock testing framework and an H2 in-memory database filled with a representative slice of my DB2 tables' data.
At the beginning of the test, I'm attempting to use every entity from a given Table to generate entries in a change-tracking table that drives my messaging.
setup:
List allExistingTestPeople = peopleRepository.findAll()
Collections.shuffle(allExistingTestPeople)
allExistingTestPeople?.each { Person person ->
Nickname nicknames = person.nicknames
nicknames?.each { Nickname nickname ->
changeTrackingRepository.save(new Change(personId: person.id, nicknameId: nickname.id, status: NEW))
}
}
Given these as my DB2 domain classes:
#Entity
#Table(name = "T_PERSON")
public class Person {
#Id
#Column(name = "P_ID")
private Integer id;
#Column(name = "P_NME")
private String name;
#OneToMany(targetEntity = Nickname.class, mappedBy = "person")
private List<Nickname> nicknames;
}
#Entity
#Table(name = "T_NICKNAME")
public class Nickname{
#EmbeddedId
private PersonNicknamePK id;
#Column(name = "N_NME")
private String nickname;
#ManyToOne(optional = false, targetEntity = Person.class)
#JoinColumn(name = "P_ID", referencedColumnName="P_ID", insertable = false, updatable = false)
private Person person;
}
#Embeddable
public class PersonNicknamePK implements Serializable {
#Column(name="P_ID")
private int personId;
#Column(name="N_ID")
private short nicknameId;
}
But I'm getting this LazyInitializationException, even though I'm reading from that OneToMany relationship within the context of a test case...
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.my.package.db2.model.Person.nicknames, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217)
at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:161)
at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:350)
I came across the advice online to annotate my test case with the #Transactional annotation, which definitely got me a little further, allowing me to read from this OneToMany relationship. However, when I then attempt to launch the Spring Batch Job I'd like to test from my when clause:
#Transactional
def "Happy path test to validate I can generate a report of changes"() {
setup:
//... See above
when:
service.launchBatchJob()
then:
//... Messages are generated
}
I'm getting the exception that a Spring Batch Job can't be launched from the context of a transaction! Even though I'm using an in-memory Job manager via ResourcelessTransactionManager and MapJobRepositoryFactoryBean, since this is just a short lived scheduled script I'm writing...
java.lang.IllegalStateException: Existing transaction detected in JobRepository. Please fix this and try again (e.g. remove #Transactional annotations from client).
at org.springframework.batch.core.repository.support.AbstractJobRepositoryFactoryBean$1.invoke(AbstractJobRepositoryFactoryBean.java:177)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy125.createJobExecution(Unknown Source)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:134)
at com.my.package.service.MyService.launchBatchJob(MyService.java:30)
The only thing that seems to work so far is if I scrap the #Transactional annotation and instead add spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true to my application-test.properties file. BUT, this doesn't seem like a very good idea, because it's not realistic - if I add this, then even if there's a bug in my code due to a lazy-initialization-exception, I'd never see it in the tests.
Sorry for the novel, hoping someone can point me in the right direction :(
EDIT:
Also here's my In-memory Spring-Batch configuration, in which I've tried turning off the transaction validation. Unfortunately, while this gets me a little further, the Spring Batch partioner's autowired EntityManager is suddenly failing to run queries in the H2 database.
#Configuration
#EnableBatchProcessing
public class InMemoryBatchManagementConfig {
#Bean
public ResourcelessTransactionManager resourceslessTransactionManager() {
ResourcelessTransactionManager resourcelessTransactionManager = new ResourcelessTransactionManager();
resourcelessTransactionManager.setNestedTransactionAllowed(true);
resourcelessTransactionManager.setValidateExistingTransaction(false);
return resourcelessTransactionManager;
}
#Bean
public MapJobRepositoryFactoryBean mapJobRepositoryFactory(ResourcelessTransactionManager txManager)
throws Exception {
MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean(txManager);
factory.setValidateTransactionState(false);
factory.afterPropertiesSet();
return factory;
}
#Bean
public JobRepository jobRepository(MapJobRepositoryFactoryBean factory) throws Exception {
return factory.getObject();
}
#Bean
public SimpleJobLauncher jobLauncher(JobRepository jobRepository) throws Exception {
SimpleJobLauncher launcher = new SimpleJobLauncher();
launcher.setJobRepository(jobRepository);
launcher.afterPropertiesSet();
return launcher;
}
#Bean
public JobExplorer jobExplorer(MapJobRepositoryFactoryBean factory) {
return new SimpleJobExplorer(factory.getJobInstanceDao(), factory.getJobExecutionDao(),
factory.getStepExecutionDao(), factory.getExecutionContextDao());
}
#Bean
public BatchConfigurer batchConfigurer(MapJobRepositoryFactoryBean mapJobRepositoryFactory,
ResourcelessTransactionManager resourceslessTransactionManager,
SimpleJobLauncher jobLauncher,
JobExplorer jobExplorer) {
return new BatchConfigurer() {
#Override
public JobRepository getJobRepository() throws Exception {
return mapJobRepositoryFactory.getObject();
}
#Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return resourceslessTransactionManager;
}
#Override
public JobLauncher getJobLauncher() throws Exception {
return jobLauncher;
}
#Override
public JobExplorer getJobExplorer() throws Exception {
return jobExplorer;
}
};
}
}
This error happens because your code will be already executed in a transaction driven by Spring Batch. So running the job in the scope of a transaction is not correct. However, if you still want to disable the transaction validation done by the job repository, you can set the validateTransactionState to false, see AbstractJobRepositoryFactoryBean#setValidateTransactionState.
That said, running the job in a transaction is not the way to fix org.hibernate.LazyInitializationException. The property spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true is there for a reason, and if it works for you, I believe it is a better approach than running the entire job in a transaction (and btw, if I had to use a transaction for that, I would narrow its scope to the minimum (for example the step) and not the entire job).
You can do transactions programmatically using TransactionTemplate to run only the "setup" inside a transaction (instead of having everything in #Transactional). Unfortunately this way the transaction will be committed and you will need to do some manual cleanup.
It can be autowired as any other bean:
#Autowired
private TransactionTemplate transactionTemplate;
...and it's used this way:
transactionTemplate.execute((transactionStatus) -> {
// ...setup...
return null; // alternatively you can return some data out of the callback
});

Is #Transactional support for NamedParameterTemplate.batchUpdate

Is #Transactional support for NamedParameterTemplate.batchUpdate?
If something went wrong during the batch execution, will it rollback as expected? Personally, I am not experienced that. That's why I am asking.
Is there any document to check #Transactional supported methods.
public class JdbcActorDao implements ActorDao {
private NamedParameterTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
#Transactional
public int[] batchUpdate(List<Actor> actors) {
return this.namedParameterJdbcTemplate.batchUpdate(
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
SqlParameterSourceUtils.createBatch(actors));
}
// ... additional methods
}
NamedParameterTemplate is just an abstraction around Jdbc. In spring it is the Transaction Manager that is responsible for managing transactions, not that you can not do it via plain JDBC but this is the spring way. Spring uses AOP internaly to inspect the annotated methods and delegates its transaction managment. But this role is separate from the NamedParameterTemplate.
So you can use it freely and annotate your methods as long as they are within a Spring managed component/bean with #Transactional

Single DAO for webservice and database

I am writing my first Spring MVC webapp and have a question about DAOs and web service requests.
Essentially my app allows the user to construct an order, which gets persisted to a database via a DAO. Later on a scheduled worker will retrieve the new orders from the database, submit them to a third-party SOAP service and update each order with some details (e.g. order number).
My controller calls the OrderService (a condensed version):
#Service
public class OrderService {
#Autowired
private OrderDao orderDao;
public List<Order> getOrderList() {
List<Order> orders = orderDao.getAllOrders();
return orders;
}
public void addNewOrder(Order order) {
orderDao.addOrder(order);
}
}
The OrderService calls the OrderDao:
#Repository
public class OrderDao extends JdbcDaoSupport {
#Autowired
public OrderDao(DataSource dataSource) {
setDataSource(dataSource);
}
public List<Order> getAllOrders() {
String sqlQuery = "SELECT id, name, status, orderNumber FROM orders";
List<Order> orders = getJdbcTemplate().query(sqlQuery, new OrderRowMapper());
return orders;
}
public int addOrder(Order order) {
String sqlQuery = "INSERT INTO orders (name, status) VALUES (?, ?)";
getJdbcTemplate().update(sqlQuery, new Object[] { order.getName(), order.getStatus() });
return getJdbcTemplate().queryForObject("SELECT LAST_INSERT_ID()", Integer.class );
}
}
The Order model looks like:
public class Order {
private int orderId;
private String name;
private String status;
private String orderNumber;
// getters and setters etc.
}
At present my OrderDao only communicates with the database to perform CRUD actions on the Order model. I am not sure whether I should create a placeOrder() method within the OrderDao as this would mean I have a single DAO that accesses both database and SOAP service, which feels wrong.
It also feels wrong to put placeOrder() in the OrderService because the service will contain a mixture of internal DAO calls and external third-party SOAP calls.
I've been reading up on interfaces but I don't think they help me here as my database DAO would contain create(), update(), delete() which wouldn't apply to a SOAP DAO.
Should I just create two DAOs: OrderDaoDatabase and OrderDaoSoap?
The point of using layered architecture is to encourage decoupling and separation of concerns. You already have the service layer to take care of business logic and data access layer (DAOs) to communicate with the database, that seems to be right. OrderService should talk to the database and OrderDAO should talk to the database.
Your scheduled worker seems to be a different class. OrderDAO can expose the order(s) data through different methods (which are required by your application). If placeOrder() is a call to external web service, it's okay to call that from an appropriate method within OrderService or a different class if required. Now, since that call isn't done at the time addOrder() is called, it probably belongs to a different method which is invoked by the scheduler. On the other hand, I don't think placeOrder() should go into OrderDAO, it should be left for what it says - data access object.

Categories