I'm just beginning with Spring framework. I'm also using DBCP pooling and i'm still not sure how to work right with jdbcTemplate.
It is best practice to reuse created/injected jdbcTemplate instance between multiple DAOs or it is right to create jdbcTemplate for each DAO ?
I'm currently using annotation approach:
public class FooDAO {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setDatasource( DataSource dataSource ) {
this.jdbcTemplate = new JdbcTemplate( dataSource );
}
}
I'm aware about JdbcDaoSupport, but I don't know how to inject datasource, because method setDatasource is marked as final.
But still, I'm not sure if is best practice to reuse created jdbcTemplate or not.
Inject it in and share it. Don't call "new"; that takes control out of the hands of the Spring bean factory.
I'm aware about JdbcDaoSupport, but I don't know how to inject datasource, because method setDatasource is marked as final.
public class JdbcDaoSupportTest extends JdbcDaoSupport {
public void insert() {
this.getJdbcTemplate().execute("insert into tb_test1 values(1,'ycl','123')");
System.out.println("complete...");
}
}
Spring call set Method, don't care whether the method is final or not.
<bean id="jdbcDaoSupportTest" class="com.xxxxx.JdbcDaoSupportTest">
<property name="dataSource" ref="dataSource" />
</bean>
then in your JdbcDaoSupportTest, you can call this.getJdbcTemplate() to get JdbcTemplate do
any operator.
try {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "select user.id as id,user.roll_no as createdId,user.name as name,user.type as company,role.role as year "
+ "from user_role join user on user.id=user_role.user_id "
+ "join role on role.id=user_role.role_id "
+ "where (user.status='ACTIVE' or user.status='ADMIN') AND user.username='" + userName + "'";
UserVo userDetails = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<UserVo>(UserVo.class));
or
Long company = jdbcTemplate.queryForObject(sql, Long.class);
or
List<UserVo> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<UserVo>(UserVo.class));
logger.info("Retrieve user details by username");
return userDetails;
} catch (Exception e) {
logger.error("error in getting UserDetails using UserName", e);
}
Related
I know how to connect to a database in usual way. What I need is to choose what database to connect at runtime.
I have a default database (used by the system) and many other options to user choose to aquire data. It implies in have no Entities or JPA mappings any way.
In older times I was using this:
try (Connection connection = DriverManager.getConnection(connectionString, user, password);
PreparedStatement preparedStatement = connection.prepareStatement(nativeQuery)) {
preparedStatement.setString( 1, coordinate );
try (ResultSet resultSet = preparedStatement.executeQuery()) {
while (resultSet.next())
result = resultSet.getString(RESULT_PARAM);
}
} catch (SQLException ex) {
CodeUtils.log(QUERY_ERROR_MSG, this);
CodeUtils.log(ex.getMessage(), this);
}
But I don't know how to port this to Spring Boot.
You can define database configuration as below-
#Configuration
public class MyDBConfig {
#Bean("db1Ds")
#Primary
#ConfigurationProperties("app.ds.db1")
public DataSource db1DataSource() {
return DataSourceBuilder.create().build();
}
#Bean("db2Ds")
#ConfigurationProperties("app.ds.db2")
public DataSource db2DataSource() {
return DataSourceBuilder.create().build();
}
#Bean("db1JdbcTemplate")
#Autowired
public JdbcTemplate db1JdbcTemplate(#Qualifier("db1Ds") DataSource ds) {
return new JdbcTemplate(ds);
}
#Bean("db2JdbcTemplate")
#Autowired
public JdbcTemplate db2JdbcTemplate(#Qualifier("db2Ds") DataSource ds) {
return new JdbcTemplate(ds);
}
}
Switch the Jdbc template based on what user selects.
Official Doc:
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html#howto-two-datasources
I have a method which has a UPDATE query and a select query. I annotated the method with #Transactional for the below use case.
For concurrent executions - if two users are updating the same table ,I need the update and select query to be run as a unit
If not using #Transactional , I am using jdbc template and i am trying to get the current connection set auto commit to false and commit to true at the end of the method
Issue 1:
Update is getting commited immediately after the statement is executed
Issue 2:
With jdbc template , unable to get the current connection used for transaction .
I have tried the below two ways to get the current connection , but it seems to be a new connection from the pool
1.Connection conn = DataSourceUtils.getConnection(template.getDataSource());
2.Connection con=template.getDataSource().getConnection();
Application deployed in WebLogic Server using using java configuration , created bean for jdbc template , datasource and transaction management and used autowiring
#Transactional
public Integer getNextId(String tablename) {
Integer nextId = null;
int swId = template.update("update " + tablename + " set swId = swId + 1");
//int swId1 = template.update("update " + tablename + " set swId = swId + 1");
if (swId == 1) {
nextId = template.queryForObject("select swId from " + tablename,
int.class);
}
return nextId;
}
}
#Scope("prototype")
#Bean
public DataSource dataSource() throws NamingException {
javax.naming.InitialContext ctx = new javax.naming.InitialContext();
DataSource dataSource = (javax.sql.DataSource) ctx.lookup(dsnName);
return dataSource;
}
#Scope("prototype")
#Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
#Scope("prototype")
#Bean
public JdbcTemplate template(DataSource dataSource) {
JdbcTemplate template = new JdbcTemplate(dataSource);
return template;
}
Expected results.
Commit should happen after all the statements in the method is executed
jdbc template need to get the active connection sed for current transaction
I upgraded a project from Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final to Spring Boot 2.1.4.RELEASE and Hibernate 5.3.9.Final. The queries are still working fine, but I'm getting LazyInitializationException with some #OneToMany class members.
First I retrieve the object, which has a reference to a #OneToMany List, from the #Transaction service. The collection is returned to the controller, and from there it goes back to Spring to be serialized into a json. The controller has #RestController, so it knows what to do.
In Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final everything was fine, even though OpenEntityManagerInView wasn't enabled by configuration and the collection wasn't loaded with EAGER mode. But in Spring Boot 2.1.4.RELEASE and Hibernate 5.3.9.Final the same thing doesn't work anymore. I've tried enabling OEMIV, by setting spring.jpa.open-in-view=true, but even this doesn't seem to work or it's being overriden somewhere.
If I enable EAGER loading mode for that collection, everything works fine.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
#Entity
#JsonSerialize(using = TemplateSerializer.class)
public class Template implements Serializable {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToOne
private ObjFormat objFormat;
#OneToOne
#JoinColumn(name = "event_id")
#OnDelete(action = OnDeleteAction.CASCADE)
private Event event;
#OneToMany
#JoinColumn(name = "category_id")
private List<Category> linkToCategories;
The problem is caused by field linkToCategories. If I configure #OneToMany(fetch = FetchType.EAGER) everything works fine.
Application configuration:
#Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) throws ClassNotFoundException {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource);
localSessionFactoryBean.setPackagesToScan("com.project.backend.model",
"com.project.backend.hibernate.converters");
return localSessionFactoryBean;
}
#Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
Later edit:
After a lot of debugging, the difference between the old and the new Hibernate functionality is in the HibernateTransactionManager. In the method doGetTransaction(), in Hibernate 4 it finds the SessionHolder object when calling
TransactionSynchronizationManager.getResource(getSessionFactory())
while in Hibernate 5 it doesn't.
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
if (sessionHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + "] for Hibernate transaction");
}
txObject.setSessionHolder(sessionHolder);
}
else if (this.hibernateManagedSession) {
try {
Session session = this.sessionFactory.getCurrentSession();
if (logger.isDebugEnabled()) {
logger.debug("Found Hibernate-managed Session [" + session + "] for Spring-managed transaction");
}
txObject.setExistingSession(session);
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException(
"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
}
}
In the method doBegin, a new session is created and set on the txObject for every request.
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Interceptor entityInterceptor = getEntityInterceptor();
Session newSession = (entityInterceptor != null ?
getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
getSessionFactory().openSession());
if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
}
txObject.setSession(newSession);
}
My experience with Hibernate is fairly small, so here I'm stuck. It's probably a configuration thing, but I can't find it.
As M. Deinum was saying, the Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final configuration was loading OpenSessionInViewFilter, which explains why all the queries were going through successfully. After configuring the same filter in Spring Boot, everything is back to normal. Add the following bean to register the filter:
#Bean
public FilterRegistrationBean<OpenSessionInViewFilter> registerOpenSessionInViewFilterBean() {
FilterRegistrationBean<OpenSessionInViewFilter> registrationBean = new FilterRegistrationBean<>();
OpenSessionInViewFilter filter = new OpenSessionInViewFilter();
registrationBean.setFilter(filter);
return registrationBean;
}
The next step is to replace plain Hibernate with JPA, and OpenSessionInViewFilter with OpenEntityManagerInViewFilter.
Thanks M. Deinum.
#xxxToMany annotations inicates that fetch type is LAZY by default. It means that you need to initialize collection your entity refers to.
Eg.
#Entity
public class Book {
#OneToMany
public List<Author> authors;
}
There is few ways to resolve this. You can modify #OneToMany annotation with:
#OneToMany(FetcType=FetchType.EAGER)
Or to make a method where you will initialize authors eg.:
public void initializeAuthors(Book book) {
Book b = em.find(Book.class, book.getId());
List<Author> authors = new ArrayList<>(b.getAuthors());
book.setAuthors(authors);
}
If you have #NamedQueries on your entities, you can do that by adding LEFT JOIN FETCH on your collections.
We currently have an application which uses multiple databases with the same schema. At the moment we're using a custom solution for switching between them based on the user's session. This works via
public final class DataSourceProxy extends BasicDataSource {
...
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getDetails() instanceof Map) {
Map<String, String> details = (Map<String, String>) auth.getDetails();
String targetUrl = details.get("database");
Connection c = super.getConnection();
Statement s = c.createStatement();
s.execute("USE " + targetUrl + ";");
s.close();
return c;
} else {
return super.getConnection();
}
}
Now we want to build a solution using AbstractRoutingDataSource. The problem is:
#Component
public class CustomRoutingDataSource extends AbstractRoutingDataSource {
#Autowired
Environment env;
#Autowired
DbDetailsRepositoy repo;
public CustomRoutingDataSource() {
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
for(DBDetails dbd : repo.findAll() {
// create DataSource and put it into the map
}
setTargetDataSources(new HashMap<Object, Object>());
}
#Override
protected Object determineCurrentLookupKey() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getDetails() instanceof Map) {
Map<String, String> details = (Map<String, String>) auth.getDetails();
return details.get("database");
}
return null;
}
}
Inside the constructor (or even via #PostConstruct) we have to fill the targetDataSources Map. But(!) for this we need the connection details which are stored in another database, which has its own DataSource and Entity Manager.
It seems like Spring can't determine the order of Bean construction, or maybe I'm just missing something. It always gives a NullPointerException when accessing the repository (which btw is a JpaRepository).
We're using Spring 3.2.3, Spring Data, Hibernate 4.2. Complete Annotation and Java-Code configuration of Spring and Spring Security.
Please help us!
Spring of course has to call the constructor before it can populate the properties. But that's not a Spring thing, that's basic Java 101 and one of the plenty downsides of using field injection.
To avoid this, simply add your dependencies to the constructor:
#Component
class CustomRoutingDataSource extends AbstractRoutingDataSource {
#Autowired
public CustomRoutingDataSource(DbDetailsRepository repo, Environment environment) {
…
}
…
}
The Spring framework provides two means of programmatic transaction management:
Using the TransactionTemplate.
Using a PlatformTransactionManager implementation directly.
The above is described here: http://static.springsource.org/spring/docs/2.0.8/reference/transaction.html
The Spring site hasnot mentioned JdbcTemplate here. As per my understanding JdbcTemplate also manages the transaction internally and this is all done in programme too.
So what's the basic difference between TransactionTemplate and JdbcTemplate?
JdbcTemplate is not a transaction manager. It's merely a helper class for native JDBC operations:
This is the central class in the JDBC core package. It simplifies the use of JDBC and helps to avoid common errors. It executes core JDBC workflow, leaving application code to provide SQL and extract results. This class executes SQL queries or updates, initiating iteration over ResultSets and catching JDBC exceptions and translating them to the generic, more informative exception hierarchy defined in the org.springframework.dao package.
TransactionTemplate by the way is also not a transaction manager, it's a
Template class that simplifies programmatic transaction demarcation and transaction exception handling.
The PlatformTransactionManager (and other subclasses of AbstractPlatformTransactionManager) is a transaction manager, as in it
determines if there is an existing transaction;
applies the appropriate propagation behavior;
suspends and resumes transactions if necessary;
checks the rollback-only flag on commit;
applies the appropriate modification on rollback (actual rollback or setting rollback-only);
triggers registered synchronization callbacks (if transaction synchronization is active).
So this class is responsible for the actual transaction handling, as opposed to the TransactionTemplate, which is to be used if you instead of declarative transaction handling you want to implement it programmetically. (see this blog, though quite outdated, you will see the difference between declarative and manual)
Quotes from Spring 3 Reference.
Note: Throughout the Spring Framework you will find other *Template classes as well: HibernateTemplate, JmsTemplate etc. They all follow the same pattern: template classes which radically reduce the amount of code you need to write, because all the so-called boilerplate code will be handled by them. Example (from here):
Without JdbcTemplate:
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void insert(Customer customer){
String sql = "INSERT INTO CUSTOMER " +
"(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
Connection conn = null;
try {
conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, customer.getCustId());
ps.setString(2, customer.getName());
ps.setInt(3, customer.getAge());
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {}
}
}
}
And with JdbcTemplate:
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void insert(Customer customer){
String sql = "INSERT INTO CUSTOMER " +
"(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update(sql, new Object[] { customer.getCustId(),
customer.getName(),customer.getAge()
});
}