SpringBoot整合JdbcTemplate
参考文章
主要内容
- JdbcTemplate其实就是用来操作数据库的,是最基本的Spring JDBC模板,是对JDBC很轻的封装,具有较高的执行效率(基于jdbc的数据操作要比hibernate那些效率都高,尤其批量处理),一般用于系统数据库并发操作压力大的项目中。
- Spring数据访问模板:在数据库操作过程中,有很多重复工作,如事务控制、资源管理和异常处理等,Spring的模板类封装了这些固定部分。
- 主要包含以下四类方法:
- execute():执行任何sql语句,一般用于执行ddl语句。
- update()、batchUpdate():执行(批量执行新增、修改、删除语句。
- query()、queryForXxx():执行查询语句。
- call():执行存储过程、函数等语句;当然,前面的三种方法其sql语句都可以带函数。
- 参考文章
首先操作数据库需要添加以下依赖
org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 在application.properties中配置数据源
spring.datasource.url = jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8 spring.datasource.username = root spring.datasource.password = root spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring-boot-starter-jdbc默认使用tomcat-jdbc数据源,推荐使用其他数据源尤其是阿里巴巴的数据池管理,请额外添加以下依赖
com.alibaba druid 1.0.19 - 连接池详情参见笔记:连接池
- 然后在Application.java配置DataSource,这里介绍两种方式
第一种:application.properties只配置jdbc驱动,数据池配置则全写在入口类
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } //Environment这个类是用来加载application.properties文件中的属性的 @Autowired private Environment env; //destroy-method="close"的作用是当数据库连接不使用的时候,就把该连接重新放到数据池中,方便下次使用调用. @Bean(destroyMethod = "close") public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(env.getProperty("spring.datasource.url")); //引用配置文件的变量 dataSource.setUsername(env.getProperty("spring.datasource.username"));//用户名 dataSource.setPassword(env.getProperty("spring.datasource.password"));//密码 dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name")); dataSource.setInitialSize(2);//初始化时建立物理连接的个数 dataSource.setMaxActive(20);//最大连接池数量 dataSource.setMinIdle(0);//最小连接池数量 dataSource.setMaxWait(60000);//获取连接时最大等待时间,单位毫秒。 dataSource.setValidationQuery("SELECT 1");//用来检测连接是否有效的sql dataSource.setTestOnBorrow(false);//申请连接时执行validationQuery检测连接是否有效 dataSource.setTestWhileIdle(true);//建议配置为true,不影响性能,并且保证安全性。 dataSource.setPoolPreparedStatements(false);//是否缓存preparedStatement,也就是PSCache return dataSource; } }
第二种:在application.properties中写全部配置,在入口类用@ConfigurationProperties直接把变量封装到类中
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=123456 #阿里巴巴连接池配置 #最大连接池数量 spring.datasource.max-active=20 #最小连接池数量 spring.datasource.min-idle=8 #获取连接时最大等待时间 spring.datasource.maxWait=60000 #初始化时建立连接的个数 spring.datasource.initialSize=2 #申请连接时执行ValidationQuery检测连接是否有效,建议为false,因为做了这个配置会降低性能;若下面那行括号为null则这一行怎么写都不生效 spring.datasource.testOnBorrow=false #用来检测连接是否有效的sql,要求是一个查询语句 spring.datasource.validationQuery="SELECT 1" #建议配置为true,不影响性能且保证安全性;作用是在ValidationQuery上加个条件:当空闲时间大于timeBetweenEvictionRunsMillis时才执行连接检测 spring.datasource.testWhileIdle=true #是否缓存preparedStatement spring.datasource.poolPreparedStatements=true
- 入口类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
@Bean(destroyMethod = "close") //destroyMethod = "close":当数据库连接不使用时就把该连接重新放到数据池中,方便下次调用
@ConfigurationProperties(prefix="spring.datasource") //直接使用配置文件中前缀为这个的属性来set DataSource的属性
public DataSource dataSource(){
return new DruidDataSource();
}
}
- Springboot会自动选择我们自己配置的这个DataSource实例
- 在dao层注入jdbcTemplate,它有很多操作数据库的方法
@Repository
public class LearnDaoImpl implements LearnDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int add(LearnResouce learnResouce) {
return jdbcTemplate.update("insert into learn_resource(author, title,url) values(?, ?, ?)",learnResouce.getAuthor(),learnResouce.getTitle(),learnResouce.getUrl());
}
}
代码实例:SpringMvc02
- 应该是跑不起来,反正看一看理解原理就是了
Spring整合JdbcTemplate
引入依赖
org.springframework spring-jdbc 4.0.0.RELEASE JdbcTemplateUtils:获取JdbcTemplate对象,单例模式
/** * @Author haien * @Description 获取JdbcTemplate对象,单例模式 * @Date 2019/2/24 **/ public class JdbcTemplateUtils { private static JdbcTemplate jdbcTemplate; /** * @Author haien * @Description 单例模式 * @Date 2019/2/24 * @Param [] * @return org.springframework.jdbc.core.JdbcTemplate **/ public static JdbcTemplate jdbcTemplate(){ if(jdbcTemplate==null) jdbcTemplate=createJdbcTemplate(); return jdbcTemplate; } /** * @Author haien * @Description 配置数据源 * @Date 2019/2/25 * @Param [] * @return org.springframework.jdbc.core.JdbcTemplate **/ private static JdbcTemplate createJdbcTemplate(){ DruidDataSource ds=new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiro"); ds.setUsername("root"); ds.setPassword("123456"); return new JdbcTemplate(ds); } }
- UserDaoImpl:使用JdbcTemplate对象
public class UserDaoImpl implements UserDao {
private JdbcTemplate jdbcTemplate = JdbcTemplateUtils.jdbcTemplate();
@Override
public User createUser(final User user) {
final String sql = "insert into sys_users(username, password, salt, locked) " +
"values(?,?,?,?)";
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection)
throws SQLException {
//没有第二个参数应该也能获取到新生id
PreparedStatement psst = connection.prepareStatement(sql, new String[] { "id" });
psst.setString(1, user.getUsername());
psst.setString(2, user.getPassword());
psst.setString(3, user.getSalt());
psst.setBoolean(4, user.getLocked());
return psst;
}
}, keyHolder);
user.setId(keyHolder.getKey().longValue());
return user;
}
@Override
public void updateUser(User user) {
String sql = "update sys_users set username=?, password=?, salt=?, locked=? where id=?";
jdbcTemplate.update(sql, user.getUsername(), user.getPassword(), user.getSalt(),
user.getLocked(), user.getId());
}
@Override
public void deleteUser(Long userId) {
String sql = "delete from sys_users where id=?";
jdbcTemplate.update(sql, userId);
}
@Override
public void correlationRoles(Long userId, Long... roleIds) {
if(roleIds == null || roleIds.length == 0) {
return;
}
String sql = "insert into sys_users_roles(user_id, role_id) values(?,?)";
for(Long roleId : roleIds) {
if(!exists(userId, roleId)) {
jdbcTemplate.update(sql, userId, roleId);
}
}
}
@Override
public void uncorrelationRoles(Long userId, Long... roleIds) {
if(roleIds == null || roleIds.length == 0) {
return;
}
String sql = "delete from sys_users_roles where user_id=? and role_id=?";
for(Long roleId : roleIds) {
if(exists(userId, roleId)) {
jdbcTemplate.update(sql, userId, roleId);
}
}
}
private boolean exists(Long userId, Long roleId) {
String sql = "select count(1) from sys_users_roles where user_id=? and role_id=?";
return jdbcTemplate.queryForObject(sql, Integer.class, userId, roleId) != 0;
}
@Override
public User findOne(Long userId) {
String sql = "select id, username, password, salt, locked from sys_users where id=?";
List userList = jdbcTemplate.query(sql,
new BeanPropertyRowMapper(User.class), userId);
if(userList.size() == 0) {
return null;
}
return userList.get(0);
}
@Override
public User findByUsername(String username) {
String sql = "select id, username, password, salt, locked from sys_users where username=?";
List userList = jdbcTemplate.query(sql,
new BeanPropertyRowMapper(User.class), username);
if(userList.size() == 0) {
return null;
}
return userList.get(0);
}
@Override
public Set findRoles(String username) {
String sql = "select role from sys_users u, sys_roles r,sys_users_roles ur " +
"where u.username=? and u.id=ur.user_id and r.id=ur.role_id";
return new HashSet(jdbcTemplate.queryForList(sql, String.class, username));
}
@Override
public Set findPermissions(String username) {
//TODO 此处可以优化,比如查询到role后,一起获取roleId,然后直接根据roleId获取即可
String sql = "select permission from sys_users u, sys_roles r, sys_permissions p, " +
"sys_users_roles ur, sys_roles_permissions rp " +
"where u.username=? and u.id=ur.user_id and r.id=ur.role_id " +
"and r.id=rp.role_id and p.id=rp.permission_id";
return new HashSet(jdbcTemplate.queryForList(sql, String.class, username));
}
}
- jdbcTemplate.queryForInt() 和 jdbcTemplate.queryForLong():返回查询结果条数,根据预估数目大小选择int或long
String sql="SELECT count(*) FROM mcp_forum_post";
Integer todayCount=jdbcTemplate.queryForInt(sql);
//或
Long todayCount=jdbcTemplate.queryForLong(sql);
- jdbcTemplate.queryForObject(String sql, Class requiredType, sql语句所需参数): 返回查询sql中指定字段,并用requiredType类型承接。要求结果集只有一条记录,否则抛异常。
String sql="select account from scpn_user where user_id="+userAccountId;
//sql指明返回account字段,故返回account字段
//account是String类,但即使account是int型,指定为String类也可以,不知是否指定为任何类都可以
String userAccount=(String)jdbcTemplate.queryForObject(sql, String.class);
- 若sql语句查询所有字段,则无论requiredType是什么,都只返回查询到的记录总数,也就是0或1,常用来判断查询是否成功。
String sql="select * from scpn_user where user_id="+userAccountId;
String userAccount=(String)jdbcTemplate.queryForObject(sql, String.class); //userAccount=1
- jdbcTemplate.queryForMap():查询一条记录,将各个字段及其值作为键值对封装进Map。
String userAccountSql="select account,create_time from scpn_user
where user_id="+userAccountId;
Map userAccountMap=(Map)jdbcTemplate.queryForMap(userAccountSql);
String userAccount= (String)userAccountMap.get("account");
String createTime= (String)userAccountMap.get("create_time").toString();
- jdbcTemplate.queryForList():查询多条记录,一条一个Map,多个Map组成一个List
String all="SELECT * FROM mcp_forum_post";
List scpnPostList = jdbcTemplate.queryForList(all);
if (scpnPostList!=null) {
for (int i = 0; i < scpnPostList.size(); i++) {
Long userAccountId = (Long) scpnPostList.get(i).get("user_id");
Long lastmodUser = (Long) scpnPostList.get(i).get("lastmod_user");
}
}
- JdbcTemplate.queryForXxx()总结
- jdbcTemplate.query(String sql,RowMapper rowMapper,sql所需参数):将查询到的结果封装进rowMapper类。
//需定义一个匿名实现类
User user = jdbcTemplate.query(sql,new RowMapper(){
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
return user;
}
});
//或者放一个自定义实现类进去
user = jdbcTemplate.query(sql, new UserRowMapper());
//实现类
public class UserRowMapper implements RowMapper {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
return user;
}
}
- 也可以使用Spring提供的RowMapper实现类BeanPropertyRowMapper:org.springframework.jdbc.core.BeanPropertyRowMapper,指定封装的类型即可。
//只能返回List
List userList = jdbcTemplate.query(sql,
new BeanPropertyRowMapper(User.class));
- 实现原理:根据sql列名/别名找对应setter,映射规则就是大写变成”_小写”.
//比如role_ids字段,User类中只有setRoleIdsStr(),故要取别名
String sql = "select id, organization_id, username, password, salt, role_ids " +
"as roleIdsStr, locked from sys_user where id=?";
List userList = jdbcTemplate.query(sql,
new BeanPropertyRowMapper(User.class), userId);
- 代码实例:ideaProjects/shiroHelloWorld/utils/JdbctemplateUtils、dao/impl/UserDaoImpl;ideaProjects/shirochapter16/dao/impl/UserDaoImpl
JdbcDaoSupport
- 使用了JdbcTemplate后,我们在书写dao层时还要每个dao都引入JdbcTemplate的实例,而继承JdbcDaoSupport后,这个类就会负责处理JdbcTemplate对象,而且会负责数据库的自动关闭。
- 方法如下:
//设置JdbcTemplate
public final void setJdbcTemplate(JdbcTemplate jdbcTemplate)
//获取JdbcTemplate
public final JdbcTemplate getJdbcTemplate()
- 操作JdbcTemplate:
getJdbcTemplate().update(sql);
示例
- PermissionDaoImpl:dao实现层,继承JdbcDaoSupport
public class PermissionDaoImpl extends JdbcDaoSupport implements PermissionDao {
@Override
public void deletePermission(Long permissionId) {
String sql = "delete from sys_roles_permissions where permission_id=?";
getJdbcTemplate().update(sql, permissionId);
}
}
spring-beans.xml:注册PermissionDaoImpl、注入DataSource以创建JdbcTemplate(不用显式创建JdbcTemplate了)
代码实例:ideaProjects/shirochapter12/dao/impl、spring-beans.xml
- 参考文章