Autowire jdbcTemplate in ObjectMother for a junit 4 integration test - java

I was wondering if I can somehow autowire jdbcTemplate in the ObjectMother for a junit 4 integration Test.
The purpose of the test is to test that if you attach a document to an employee, you cannot attach afterwards a document with the same name.
For that I made an EmployeeMother that has a method that creates an Employee and a method that inserts it, using jdbcTemplate. If I try to autowire the jdbcTemplate in EmployeeMother it is null(test returns NPE on the first update). It seems I only have access to the applicationContexts from the test itself.
Currently I set it from the test itself, but I would like not to, because I will create more ObjectMothers for different objects and would like not to set the jdbcTemplate for all of them.
Here are the two classes (I deleted the company name from the package and imports):
EmployeeMother:
package com.domain.objMother;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import com.domain.vo.Company;
import com.domain.vo.Contact;
import com.domain.vo.Employee;
import com.domain.vo.Identification;
import com.domain.vo.Role;
public class EmployeeMother {
private final Log log = LogFactory.getLog(getClass());
protected JdbcTemplate jdbcTemplate;
private Employee empJohnDoe;
/**
*
* #return returns an admin user with username djohn
*/
public Employee getEmpJohnDoe() {
empJohnDoe = new Employee();
empJohnDoe.setUserName("djohn");
Role role = new Role();
//as only the id of the role is not nullable I set it as 1 = admin
role.setId(new Long(1));
empJohnDoe.setRole(role);
empJohnDoe.setCompany(new Company());
Identification identity = new Identification();
identity.setFirstName("John");
identity.setLastName("Doe");
identity.setContact(new Contact());
empJohnDoe.setIdentity(identity);
return empJohnDoe;
}
public void setEmpJohnDoe(Employee empJohnDoe) {
this.empJohnDoe = empJohnDoe;
}
/**
* Important! this insert does not cover some details of the Employee:
* It inserts null in the following columns:
* pswd,
* image,
* cnt_id, - should be a list of associated contacts
* salt,
* is_active,
* default_work_hours
* The insert in TAB_IDENTIFICATIONS triggers TRIG_IDNT that inserts stuff in an audit table
* For it to work we need to register a logged user
* That's why we call PAC_SECURITY.PRO_SETCTX('emp_user_name','adminUserName'); (i used an admin)
* I preferred doing this rather than inserting djohn in TAB_EMPLOYEES,
* registering djohn as logged then inserting an identity in TAB_IDENTIFICATIONS
* and then updating djohn with the new identity
* #param emp - Employee to be inserted
*/
public void insert(Employee emp){
jdbcTemplate.update("call PAC_SECURITY.PRO_SETCTX('emp_user_name','adminUserName')");
Long identityId = jdbcTemplate.queryForObject("select max(ti.ID)+1 from tab_identifications ti", Long.class);
emp.getIdentity().setId(identityId);
jdbcTemplate.update(""+
" insert into tab_identifications ("+
" id, first_name, middle_name, last_name, cnp, ci_char, ci_number, birth_date, invalidity,"+
" cas_name, ci_issue_date, ci_issuer, cnt_id"+
" )" +
" values (?,?,?,?,?,?,?,?,?,?,?,?,?)",
new Object[]{emp.getIdentity().getId(), emp.getIdentity().getFirstName(), emp.getIdentity().getMiddleName(),
emp.getIdentity().getLastName(), emp.getIdentity().getCnp(), emp.getIdentity().getIdCardSerial(),
emp.getIdentity().getIdCardNumber(), emp.getIdentity().getBirthDate(),
emp.getIdentity().getInvalidity(), emp.getIdentity().getCAS(), emp.getIdentity().getCiIssueDate(),
emp.getIdentity().getCiIssuer(), emp.getIdentity().getContact().getId()}
);
Long id = jdbcTemplate.queryForObject("select max(te.ID)+1 from tab_employees te", Long.class);
emp.setId(id);
jdbcTemplate.update(""+
" insert into tab_employees (id, user_name, code, pswd, idnt_id, role_id, comp_id, image, "+
" hire_date, cnt_id, salt, is_expired, is_active, default_work_hours "+
" )" +
" values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
new Object[]{emp.getId(), emp.getUserName(), emp.getCode(), null, emp.getIdentity().getId(),
emp.getRole().getId(), emp.getCompany().getId(), null, emp.getHireDate(),
null, null, emp.getIsExpired(), null, null
}
);
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
HomeEmployeeServiceImplIntegrationTest:
package com.employee.service.impl;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.domain.vo.Document;
import com.domain.vo.Employee;
import com.domain.objMother.EmployeeMother;
import com.employee.service.HomeEmployeeService;
import com.util.annotations.TransactionalDevTest;
import static org.junit.Assert.*;
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionalDevTest
public class HomeEmployeeServiceImplIntegrationTest {
#Autowired
protected JdbcTemplate jdbcTemplate;
#Autowired
HomeEmployeeService homeEmployeeService;
EmployeeMother empMother = new EmployeeMother();
Employee empJohnDoe;
#Before
public void beforeEachTest() throws Exception {
empMother.setJdbcTemplate(jdbcTemplate);
empJohnDoe = empMother.getEmpJohnDoe();
empMother.insert(empJohnDoe);
}
/**
* You should not be able to add a document with the same name
* <code>uploadDocument</code> should not insert the document if it has the same name
*/
#Test
public void shouldNotBeAbleToAddSameDoc(){
Document doc = new Document();
Long id = jdbcTemplate.queryForObject("select max(td.ID)+1 from tab_documents td", Long.class);
doc.setId(id);
doc.setFileName("SameOldDocument");
homeEmployeeService.uploadDocument(empJohnDoe.getIdentity(), doc);
id = jdbcTemplate.queryForObject("select max(td.ID)+1 from tab_documents td", Long.class);
doc.setId(id);
homeEmployeeService.uploadDocument(empJohnDoe.getIdentity(), doc);
Long docNo = jdbcTemplate.queryForObject("select count(id) from tab_documents td where doc_file_name = '" + doc.getFileName() + "'", Long.class);
if(docNo.compareTo(new Long(2)) == 0){
assertEquals("I was able to add a document twice with the same name!", new Long(1), docNo);
}
else{
assertEquals("Something went wrong when adding two documents with the same name! The document should be added once or twice, but the result is different!", new Long(1), docNo);
}
}
TransactionalDevTest is where I define all the applicationContexts used.
The code above works, but I would like to separate EmployeeMother's code and add IdentificationMother and probably DocumentMother each with it's object and insert. I would also like not to set jdbcTemplate for each ObjectMother (things can become ambiguous, some setting jdbcTemplate from the test, some setting it from another ObjectMother).
Thanks in advance.

I was helped by a collegue and this is what I did:
I made an annotation for ObjectMothers:
package com.util.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface ObjectMother {}
I added this annotation to my EmployeeMother:
#ObjectMother
public class EmployeeMother {
and autowired my jdbcTemplate in EmployeeMother (also deleted the getter and setter)
#Autowired
protected JdbcTemplate jdbcTemplate;
I added to an applicationContext:
The xmlns:
xmlns:context="http://www.springframework.org/schema/context"
with schema location
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
and
<context:component-scan base-package="com.domain.objMother">
<context:include-filter type="annotation" expression="com.util.annotations.ObjectMother"/>
</context:component-scan>
This searches all classes annotated with ObjectMother and adds them to the applicationContext (so that you don't have to add them all one by one).
And finnaly I used EmployeeMother autowired in HomeEmployeeServiceImplIntegrationTest and deleted any reference to jdbcTemplate from EmployeeMother in the same class:
#Autowired
EmployeeMother empMother;
Final classes:
EmployeeMother:
package com.domain.objMother;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import com.domain.vo.Company;
import com.domain.vo.Contact;
import com.domain.vo.Employee;
import com.domain.vo.Identification;
import com.domain.vo.Role;
import com.util.annotations.ObjectMother;
#ObjectMother
public class EmployeeMother {
private final Log log = LogFactory.getLog(getClass());
#Autowired
protected JdbcTemplate jdbcTemplate;
private Employee empJohnDoe;
/**
*
* #return returns an admin user with username djohn
*/
public Employee getEmpJohnDoe() {
empJohnDoe = new Employee();
empJohnDoe.setUserName("djohn");
Role role = new Role();
//as only the id of the role is not nullable I set it as 1 = admin
role.setId(new Long(1));
empJohnDoe.setRole(role);
empJohnDoe.setCompany(new Company());
Identification identity = new Identification();
identity.setFirstName("John");
identity.setLastName("Doe");
identity.setContact(new Contact());
empJohnDoe.setIdentity(identity);
return empJohnDoe;
}
public void setEmpJohnDoe(Employee empJohnDoe) {
this.empJohnDoe = empJohnDoe;
}
/**
* Important! this insert does not cover some details of the Employee:
* It inserts null in the following columns:
* pswd,
* image,
* cnt_id, - should be a list of associated contacts
* salt,
* is_active,
* default_work_hours
* The insert in TAB_IDENTIFICATIONS triggers TRIG_IDNT that inserts stuff in an audit table
* For it to work we need to register a logged user
* That's why we call PAC_SECURITY.PRO_SETCTX('emp_user_name','adminUserName'); (i used an admin)
* I preferred doing this rather than inserting djohn in TAB_EMPLOYEES,
* registering djohn as logged then inserting an identity in TAB_IDENTIFICATIONS
* and then updating djohn with the new identity
* #param emp - Employee to be inserted
*/
public void insert(Employee emp){
jdbcTemplate.update("call PAC_SECURITY.PRO_SETCTX('emp_user_name','adminUserName')");
Long identityId = jdbcTemplate.queryForObject("select max(ti.ID)+1 from tab_identifications ti", Long.class);
emp.getIdentity().setId(identityId);
jdbcTemplate.update(""+
" insert into tab_identifications ("+
" id, first_name, middle_name, last_name, cnp, ci_char, ci_number, birth_date, invalidity,"+
" cas_name, ci_issue_date, ci_issuer, cnt_id"+
" )" +
" values (?,?,?,?,?,?,?,?,?,?,?,?,?)",
new Object[]{emp.getIdentity().getId(), emp.getIdentity().getFirstName(), emp.getIdentity().getMiddleName(),
emp.getIdentity().getLastName(), emp.getIdentity().getCnp(), emp.getIdentity().getIdCardSerial(),
emp.getIdentity().getIdCardNumber(), emp.getIdentity().getBirthDate(),
emp.getIdentity().getInvalidity(), emp.getIdentity().getCAS(), emp.getIdentity().getCiIssueDate(),
emp.getIdentity().getCiIssuer(), emp.getIdentity().getContact().getId()}
);
Long id = jdbcTemplate.queryForObject("select max(te.ID)+1 from tab_employees te", Long.class);
emp.setId(id);
jdbcTemplate.update(""+
" insert into tab_employees (id, user_name, code, pswd, idnt_id, role_id, comp_id, image, "+
" hire_date, cnt_id, salt, is_expired, is_active, default_work_hours "+
" )" +
" values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
new Object[]{emp.getId(), emp.getUserName(), emp.getCode(), null, emp.getIdentity().getId(),
emp.getRole().getId(), emp.getCompany().getId(), null, emp.getHireDate(),
null, null, emp.getIsExpired(), null, null
}
);
}
}
HomeEmployeeServiceImplIntegrationTest:
package com.employee.service.impl;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.domain.vo.Document;
import com.domain.vo.Employee;
import com.domain.objMother.EmployeeMother;
import com.employee.service.HomeEmployeeService;
import com.util.annotations.TransactionalDevTest;
import static org.junit.Assert.*;
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionalDevTest
public class HomeEmployeeServiceImplIntegrationTest {
#Autowired
protected JdbcTemplate jdbcTemplate;
#Autowired
HomeEmployeeService homeEmployeeService;
#Autowired
EmployeeMother empMother;
Employee empJohnDoe;
#Before
public void beforeEachTest() throws Exception {
empJohnDoe = empMother.getEmpJohnDoe();
empMother.insert(empJohnDoe);
}
/**
* You should not be able to add a document with the same name
* <code>uploadDocument</code> should not insert the document if it has the same name
*/
#Test
public void shouldNotBeAbleToAddSameDoc(){
Document doc = new Document();
Long id = jdbcTemplate.queryForObject("select max(td.ID)+1 from tab_documents td", Long.class);
doc.setId(id);
doc.setFileName("SameOldDocument");
homeEmployeeService.uploadDocument(empJohnDoe.getIdentity(), doc);
id = jdbcTemplate.queryForObject("select max(td.ID)+1 from tab_documents td", Long.class);
doc.setId(id);
homeEmployeeService.uploadDocument(empJohnDoe.getIdentity(), doc);
Long docNo = jdbcTemplate.queryForObject("select count(id) from tab_documents td where doc_file_name = '" + doc.getFileName() + "'", Long.class);
if(docNo.compareTo(new Long(2)) == 0){
assertEquals("I was able to add a document twice with the same name!", new Long(1), docNo);
}
else{
assertEquals("Something went wrong when adding two documents with the same name! The document should be added once or twice, but the result is different!", new Long(1), docNo);
}
}
}

Related

How to write a query to get distinct values from mongodb collection?

I need to get distinct values from a collection. Those data is in a field of a collection. It means I need to get set of name from the user collection.I tried it using cmd and I get the result as I need. But I can't understand how to write the query in the spring file.Since I'm new to java I have not enough knowledge how to handle this.
Given below is a image of the database collection
Services.java
package limark.internal.css.services;
import limark.internal.css.core.model.User;
import limark.internal.css.exceptions.ResourceNotFoundException;
import limark.internal.css.persistence.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Array;
import java.time.OffsetDateTime;
import java.util.List;
import static limark.internal.css.core.MessageConstants.USER_NOT_FOUND;
#Service
#Slf4j
public class UserService {
private final UserRepository userRepository;
#Autowired
public UserService(final UserRepository userRepository){
this.userRepository = userRepository;
}
/**
* Creates a new User
* #param user object
* #return user object
*/
public User create(User user){
user.setCreatedByLoginId("");
user.setCreatedTs(OffsetDateTime.now());
return userRepository.save(user);
}
/**
* Returns list of users.
*/
public List<User> findUsers(){
return userRepository.findAll();
}
/**
* Find user by id
* #param id userId
* #return user
*/
public User findById(#NotNull String id){
return userRepository.findById(id).orElseThrow(()->new ResourceNotFoundException(USER_NOT_FOUND,
id));
}
public User update(#NotNull User user){
user.setLastUpdatedByLoginId("");
user.setLastUpdatedTs(OffsetDateTime.now());
return userRepository.save(user);
}
/**
* sets a user to not active on delete with the given id
*
* #param userId valid user id
* #throws ResourceNotFoundException when user is not found
*/
public void deleteUser(String userId) {
User user =
userRepository
.findById(userId)
.orElseThrow(() -> new ResourceNotFoundException(USER_NOT_FOUND, userId));
user.setActive(false);
user.setLastUpdatedByLoginId("");
user.setLastUpdatedTs(OffsetDateTime.now());
userRepository.save(user);
log.info("The user {} is deleted successfully.", user.getId());
}
/**
* Returns list of users.
*/
public Array findUsersList(){
return userRepository.distinct( "firstName" );
}
}
I need to add this query inside
/**
* Returns list of users.
*/
public Array findUsersList(){
return userRepository.distinct( "firstName" );
}
You can introduce a method in the UserRepository to retrieve the distinct firstName field values and return a List<String>.
public interface UserRepository extends MongoRepository<User, String> {
#Aggregation(pipeline = { "{ '$group': { '_id' : '$firstName' } }" })
List<String> findDistinctFirstNames();
}
The call to get the list of distinct first names:
List<String> firstNamesDistinct = userRepository.findDistinctFirstNames();
This worked fine using Spring Data MongoDB v2.4 and MongoDB v4.2.

JdbcTemplate queryForMap get() returns null for valid key

A simple JdbcTemplate.queryForMap() call on MySQL table yields this strange explainable behavior. The map output returns null for get() and expected results for keySet() and values().
Edited:
-Posted full class code
- Appended query with RowMapper() implementation.
Here's the table schema:
create table Product(
id int not null primary key auto_increment,
`name` varchar(30),
price decimal(10,2)
);
mysql> select * from Product;
+----+--------+-------+
| id | name | price |
+----+--------+-------+
| 1 | iPhone | 10.00 |
+----+--------+-------+
1 row in set (0.00 sec)
Complete Java class:
package com.test1;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.dbcp2.BasicDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
/**
*
* #author user2176499
*
*/
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) throws Exception {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/testdb");
dataSource.setUsername("user");
dataSource.setPassword("password");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
Map<String, Object> row = jdbcTemplate.queryForMap("select * from Product where id=?", 1);
System.out.println(row.getClass());
System.out.println(row.keySet());
System.out.println(row.values());
System.out.println(row.containsKey("name"));
System.out.println(row.get("name"));
Iterator<String> iterator = row.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
System.out.println(key + " - " + row.get(key));
}
System.out.println("==================");
List<Map<String, Object>> rows = jdbcTemplate.query("select * from Product where id=?", new Object[] { 1 },
new RowMapper<Map<String, Object>>() {
#Override
public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
return Map.ofEntries(Map.entry("id", rs.getInt("id")), Map.entry("name", rs.getString("name")),
Map.entry("price", rs.getDouble("price")));
}
});
System.out.println(rows);
}
}
Output:
class org.springframework.util.LinkedCaseInsensitiveMap
[id, name, price]
[1, iPhone, 10.00]
false
null
id - null
name - null
price - null
==================
[{name=iPhone, price=10.0, id=1}]
Spring Version: spring-jdbc-5.2.2-release

Why does a #Query annotated method require a body?

I'm new to Spring.
I wrote a class:
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class Kontroller
{
#Query( value="select * from teszt"
, nativeQuery = true
)
List<Teszt> osszesSor(); // <-- error
//.....
#RequestMapping("/szam")
public #ResponseBody String szamossag()
{
List<Teszt> sokasag = osszesSor();
return("számosság="+sokasag.size());
}
}
And it says (it = the IDE (STS), and at runtime when I call osszesSor()):
This method requires a body instead of a semicolon.
The Teszt class is:
import java.io.Serializable;
import javax.persistence.*;
#Entity
#IdClass(Teszt.class)
public class Teszt implements Serializable {
#Id
private String kulcs;
#Id
private String ertek;
//getters, setters
}
It's a very simple table (that's why it becomes very complex in spring, because it has not a one-column key):
create table TESZT
(
KULCS VARCHAR2(2000) not null,
ERTEK VARCHAR2(2000) not null,
constraint TESZT_UN
unique (KULCS, ERTEK)
)
Now I know.
I had to create a repo interface:
public interface TesztRepo extends Repository<Teszt, Teszt>
{
#Query( value="select * from teszt"
, nativeQuery = true
)
List<Teszt> sokasag();
//...
}
and autowire it in the controller.
#Autowired
TesztRepo dao;
//...
List<Teszt> sokasag = dao.sokasag();
Not that complex.

Connecting Spring Boot JDBCTemplate to SQL Server (MSSQL)

I'm very new to Spring Boot and I'm having trouble trying to set my project up so that it can communicate to SQL Server - more specifically, my JDBCTemplate instance variable is null and for some reason isn't being 'autowired' with the datasource I've specified in my application.properties file. These are the steps I've taken so far:
Using STS I created a new Spring Boot project using the Spring Start Project template.
I selected Gradle to by the 'Type' and ticked JDBC.
I then followed the following tutorial to create an abstract interface (DAO) to SQL Server (http://www.tutorialspoint.com/spring/spring_jdbc_example.htm).
If you scroll down the tutorial page to the MainApp.java bit, the first 4 lines of the main method I did not use - because I don't have a beans.xml file. This is where I presume Spring Boot's #Autowired annotation comes in and creates my beans for me?
I downloaded the SQL Server jar file from Microsoft and added it to my project as an external JAR by right clicking on my project -> Build Path -> Configure Build Path -> Add External JARs..'. Doing this removed an error I had which indicated that the driverClassName I specified in my application.properties file couldn't be found.
I'll start by displaying the contents of my 'application.properties' file:
spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=testdb
spring.datasource.username=sa
spring.datasource.password=myPassword
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerConnection
spring.datasource.initialize=true
Below is my 'JDBCTemplate.java' class which contains my CRUD methods:
package demo;
import java.util.List;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
public class BranchJDBCTemplate implements BranchDAO {
private DataSource dataSource;
#Autowired
protected JdbcTemplate jdbcTemplateObject;
#Autowired
#Override
public void setDataSource(DataSource ds) {
this.dataSource = ds;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
#Override
public void create(String name) {
String SQL = "insert into branches (name) values (?)";
jdbcTemplateObject.update(SQL, name);
System.out.println("Created Record Name = " + name);
return;
}
#Override
public Branch getBranch(Integer id) {
String SQL = "select * from branches where id = ?";
Branch student = jdbcTemplateObject.queryForObject(SQL,
new Object[]{id}, new BranchMapper());
return student;
}
#Override
public List<Branch> listBranches() {
String SQL = "select * from branches";
List <Branch> branches = jdbcTemplateObject.query(SQL, new BranchMapper());
return branches;
}
#Override
public void delete(Integer id) {
String SQL = "delete from branches where id = ?";
jdbcTemplateObject.update(SQL, id);
System.out.println("Deleted Record with ID = " + id );
return;
}
#Override
public void update(Integer id, String name) {
String SQL = "update Student set name = ? where id = ?";
jdbcTemplateObject.update(SQL, id);
System.out.println("Updated Record with ID = " + id );
return;
}
}
And finally, here is my 'CustController.java' class which contains the request mapping where I use the JDBCTemplate class to perform a database operation:
package demo;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class CustController {
#RequestMapping("/customer")
public Cust customer(#RequestParam(value="name", required=false, defaultValue="World") String name) {
BranchJDBCTemplate branchTemplate = new BranchJDBCTemplate();
List<Branch> branchesList = branchTemplate.listBranches();
for (Branch branch : branchesList) {
System.out.print("ID : " + branch.getId());
}
return new Cust(12, "Test", "Test");
}
}
The issue I'm encountering as mentioned previously is that my jdbcTemplateObject instance ...
protected JdbcTemplate jdbcTemplateObject;
is null and therefore throwing an exception on the following line:
List <Branch> branches = jdbcTemplateObject.query(SQL, new BranchMapper());
It isn't being initialised automatically, can anyone point out what I'm doing wrong?
Many thanks!
Tony
You are right, you need to have beans.xml with datasource configured in it.
In CustController class customer() method, you are using new operator as:
BranchJDBCTemplate branchTemplate = new BranchJDBCTemplate();
and so this branchTemplate instance is not spring manged and so datasource is not autowired resulting in null value of jdbctemplate.
Instead use the annotatioan as:
#Repository("branchDao")
public class BranchJDBCTemplate implements BranchDAO {
...
}
and access branchTemplate in CustController as:
#RestController
public class CustController {
#Autowired
#Qualifier("branchDao")
BranchJDBCTemplate branchTemplate;
...
}
try using the following in your application.properties file
spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=testdb;integratedSecurity=false;

Hibernate - Auto or Manual ID - On cluster environment

I have an situation where I need to save a hibernate object, but I am not sure whether an ID would be assigned by calling app (using special logic with in a range - unique) or not.
If the ID is not assigned, I need hibernate to generate an ID higher than the possible ID range which the app would input (i know the range). Else should go with what using app inputs.
I am working on MySQL - checking to see if I can custom generator like below
public class MyDOIdGenerator extends IdentityGenerator{
#Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
if ((((MyDO) obj).getId()) == null) {
Serializable id = super.generate(session, obj) ;
return id;
} else {
return ((MyDO) obj).getId();
}
}
}
But my problem is, I dont know how the super.generate would behave in a clustered environment. Would it maintain the ID synchronization across servers? How do I specify the number to start from? (Because I need to exclude the app using id range when having hibernate generate it)
Please help
Thanks
In the DB, set your sequence to start with a specific number (beginning of the range you mentioned) and then use something like this for your ID annotation, it will ensure hibernate will use the next ID in the sequence and you won't have to worry about clustered/non-clustered environment issue:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
I finally implemented this using a table in database as below
package com.yahoo.mb.do;
import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.IdentityGenerator;
import com.yahoo.sm.ads.common.log.Logger;
public class MyDOGenerator extends IdentityGenerator{
private static final Class clazz = MethodHandles.lookup().lookupClass();
private static final Logger logger = Logger.getLogger(clazz);
private static String selectQuery = "SELECT nextval FROM identity_value WHERE name='MY_DO' FOR UPDATE";
private static String updateQuery = "UPDATE identity_value SET nextval=?, last_updated=? WHERE name='MY_DO'";
#Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
if ((((MyDO) obj).getId()) == null) {
Connection connection = session.connection();
try {
PreparedStatement selectStatement = connection.prepareStatement(selectQuery);
PreparedStatement updateStatement = connection.prepareStatement(updateQuery);
logger.info(clazz, "generate", "Generating nextval");
ResultSet rs = selectStatement.executeQuery();
if (rs.next()) {
Long id = rs.getLong("nextval");
logger.info(clazz, "generate", "Generated nextval: " + id);
updateStatement.setLong(1, id+1);
updateStatement.setDate(2, new java.sql.Date(new java.util.Date().getTime()));
logger.info(clazz, "generate", "Updating nextval: " + id);
updateStatement.executeUpdate();
return id;
}
} catch (SQLException e) {
logger.error(clazz, "generate", "Error generating ID" + e);
throw new HibernateException("Unable to generate MyDO id value");
}
return null;
} else {
return ((MyDO) obj).getId();
}
}
}

Categories

Resources