前提: 上一节,完成了Spring MVC无DB Demo, 这一节会引入jdbcTemplate来构建数据库查询
1. 准备:
安装Mysql 5.7.21,在mysql bench中可以使用SQL查询到自己安装的version:
select version();
2. 配置jdbcTemplate所需数据库信息
(1) 使用jdbc.properties文件来配置数据库信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/estore
jdbc.username=clops
jdbc.password=cscocmse
(2) jdbcTemplate会选用dbcp数据库连接池作为DataSource,使用spring-datasource.xml配置数据库连接池详细信息
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="10" />
<property name="maxActive" value="1000" />
</bean>
<context:property-placeholder location="classpath:jdbc.properties"/>
</beans>
其中<context:property-placeholder location>是为了让spring找到jdbc.properties文件
(3) 在spring-dao.xml中使用spring IOC 将datasource注入到jdbcTemplate实例中:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
(4) 需要在web.xml中配置spring的contextloarderlistener以及 spring配置文件路径,便于tomcat启动加载
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:spring/*.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
(5) 文件目录格式:
3. 在java中可以正常使用jdbcTemplate做数据库的增删该查动作了:
(1) 创建dao的package, 在dao中创建Interface UserDao, 创建实现类UserDaoImp
UserDao:
package com.cisco.eStore.dao;
import com.cisco.eStore.model.User;
import java.util.List;
public interface UserDao {
public void saveOrUpdate(User user);
public void delete(int uuid);
public User get(int uuid);
public List<User> list();
}
UserDaoImp:
package com.cisco.eStore.dao;
import com.cisco.eStore.dao.UserDao;
import com.cisco.eStore.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class UserDaoImp implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public User get(int uuid) {
List<Map<String, Object>> list = jdbcTemplate.queryForList("select name from user");
User user = new User();
for(int i=0; i<list.size(); i++){
Map<String, Object> rs= list.get(i);
user.setName((String)rs.get("name"));
}
return user;
}
public void saveOrUpdate(User user) {
List list = jdbcTemplate.queryForList("select name from user");
}
public void delete(int uuid) { }
public List<User> list() {
return null;
}
}
这里Spring还提供了一种类似ORM的实现方式,就是手工写RowMapping,来将查询结果映射成相应的对象,具体方法如下:
建立UserRowMapper类:
package com.cisco.eStore.entity;
import com.cisco.eStore.model.User;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.lang.Nullable;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserRowMapper implements RowMapper<User> {
@Nullable
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setName(resultSet.getString("name"));
user.setPassword(resultSet.getString("password"));
return user;
}
}
将上述jdbcTemplate.query加入RowMapper对象,返回值即为RowMapper中mapRow()定义的返回值,如:
public User get(int uuid) {
List<User> list = jdbcTemplate.query("select name, password from user", new UserRowMapper());
return list.get(0);
}
(2) 在Controller中引入UserDao自动注入实例,同时调用UserDao.get(uuid)方法,这样第一个spring mvc的数据库完成。
HelloworldController:
package com.cisco.eStore.controller;
import com.cisco.eStore.dao.UserDao;
import com.cisco.eStore.exception.BusinessException;
import com.cisco.eStore.model.User;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/admin")
public class HelloWorldController {
static Logger logger = Logger.getLogger(HelloWorldController.class);
@Autowired
private UserDao userDao;
@RequestMapping(value = "/user", method = RequestMethod.GET)
public ModelAndView user(){
return new ModelAndView("user", "command", new User());
}
@RequestMapping(value = "/addUser", method = RequestMethod.POST)
@ExceptionHandler({BusinessException.class})
public String addUser(@ModelAttribute("SpringWeb")User user, ModelMap model){
if(StringUtils.isEmpty(user.getName())){
throw new BusinessException("The user Name is null");
}
logger.info("Hello world log test");
User users = userDao.get(1);
model.addAttribute("name", user.getName());
model.addAttribute("password", user.getPassword());
model.addAttribute("email", user.getEmail());
return "result";
}
}
4. 思考:
(1) DataSource里的数据库连接池是否只初始化一份?
R: 默认配置在spring-datasource.xml中的datasource, Scope是Singleton(单例),也就是说在这个spring ioc 容器中,只有一份DataSource可供使用, 当然你可以选择配置成Prototype类型,不过针对每个需要注入datasource的对象都会重新生成一个实例,可能会产生意想不到的错误。
(2) JdbcTemplate里如何使用connection, 每个实例都会建立一个connection么,如何保证数据库连接池的分配连接功能?如何释放连接?
R: JdbcTemplate.queryforList()底层源码,使用的还是最原始的数据库查询方式,代码如下:
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
Statement stmt = null;
Object var11;
try {
stmt = con.createStatement();
this.applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
this.handleWarnings(stmt);
var11 = result;
} catch (SQLException var9) {
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, this.getDataSource());
con = null;
throw this.translateException("StatementCallback", sql, var9);
} finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, this.getDataSource());
}
return var11;
}
在获取Connection的时候,会多加一步事务判断
(3) Spring默认的模式 是Singleton 单例模式,还是每个注入都是新new的对象?