Spring-JdbcTemplate详解,这都看不懂就安心去当个咸鱼吧!

org.springframework
spring-jdbc
5.2.0.RELEASE


javax.annotation
javax.annotation-api
1.3.2


com.zaxxer
HikariCP
3.4.2


org.hsqldb
hsqldb
2.5.0

AppConfig中,我们需要创建以下几个必须的Bean:

@Configuration
@ComponentScan
@PropertySource(“jdbc.properties”)
public class AppConfig {

@Value(“${jdbc.url}”)
String jdbcUrl;

@Value(“${jdbc.username}”)
String jdbcUsername;

@Value(“${jdbc.password}”)
String jdbcPassword;

@Bean
DataSource createDataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(jdbcUrl);
config.setUsername(jdbcUsername);
config.setPassword(jdbcPassword);
config.addDataSourceProperty(“autoCommit”, “true”);
config.addDataSourceProperty(“connectionTimeout”, “5”);
config.addDataSourceProperty(“idleTimeout”, “60”);
return new HikariDataSource(config);
}

@Bean
JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}

在上述配置中:

  • 通过@PropertySource(“jdbc.properties”)读取数据库配置文件;
  • 通过@Value(“${jdbc.url}”)注入配置文件的相关配置;
  • 创建一个DataSource实例,它的实际类型是HikariDataSource,创建时需要用到注入的配置;
  • 创建一个JdbcTemplate实例,它需要注入DataSource,这是通过方法参数完成注入的。

最后,针对HSQLDB写一个配置文件jdbc.properties

数据库文件名为testdb:

jdbc.url=jdbc:hsqldb:file:testdb

Hsqldb默认的用户名是sa,口令是空字符串:

jdbc.username=sa
jdbc.password=

mysql配置

数据库文件名为testdb:

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springjdbc?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false

jdbc.user=root
jdbc.password=1234

可以通过HSQLDB自带的工具来初始化数据库表,这里我们写一个Bean,在Spring容器启动时自动创建一个users表:

@Component
public class DatabaseInitializer {
@Autowired
JdbcTemplate jdbcTemplate;

@PostConstruct
public void init() {
jdbcTemplate.update(“CREATE TABLE IF NOT EXISTS users (” //

  • "id BIGINT IDENTITY NOT NULL PRIMARY KEY, " //
  • "email VARCHAR(100) NOT NULL, " //
  • "password VARCHAR(100) NOT NULL, " //
  • "name VARCHAR(100) NOT NULL, " //
  • “UNIQUE (email))”);
    }
    }

现在,所有准备工作都已完毕。我们只需要在需要访问数据库的Bean中,注入JdbcTemplate即可:

@Component
public class UserService {
@Autowired
JdbcTemplate jdbcTemplate;

}

JdbcTemplate 用法

Spring提供的JdbcTemplate采用Template模式,提供了一系列以回调为特点的工具方法,目的是避免繁琐的try...catch语句。

我们以具体的示例来说明JdbcTemplate的用法。

首先我们看T execute(ConnectionCallback<T> action)方法,它提供了Jdbc的Connection供我们使用:

public User getUserById(long id) {
// 注意传入的是ConnectionCallback:
return jdbcTemplate.execute((Connection conn) -> {
// 可以直接使用conn实例,不要释放它,回调结束后JdbcTemplate自动释放:
// 在内部手动创建的PreparedStatement、ResultSet必须用try(…)释放:
try (var ps = conn.prepareStatement(“SELECT * FROM users WHERE id = ?”)) {
ps.setObject(1, id);
try (var rs = ps.executeQuery()) {
if (rs.next()) {
return new User( // new User object:
rs.getLong(“id”), // id
rs.getString(“email”), // email
rs.getString(“password”), // password
rs.getString(“name”)); // name
}
throw new RuntimeException(“user not found by id.”);
}
}
});
}
`
也就是说,上述回调方法允许获取Connection,然后做任何基于Connection的操作。

我们再看T execute(String sql, PreparedStatementCallback<‘T’> action)的用法:

public User getUserByName(String name) {
// 需要传入SQL语句,以及PreparedStatementCallback:
return jdbcTemplate.execute(“SELECT * FROM users WHERE name = ?”, (PreparedStatement ps) -> {
// PreparedStatement实例已经由JdbcTemplate创建,并在回调后自动释放:
ps.setObject(1, name);
try (var rs = ps.executeQuery()) {
if (rs.next()) {
return new User( // new User object:
rs.getLong(“id”), // id
rs.getString(“email”), // email
rs.getString(“password”), // password
rs.getString(“name”)); // name
}
throw new RuntimeException(“user not found by id.”);
}
});
}

最后,我们看T queryForObject(String sql, Object[] args, RowMapper<'T'> rowMapper)方法

public User getUserByEmail(String email) {
// 传入SQL,参数和RowMapper实例:
return jdbcTemplate.queryForObject(“SELECT * FROM users WHERE email = ?”, new Object[] { email },
(ResultSet rs, int rowNum) -> {
// 将ResultSet的当前行映射为一个JavaBean:
return new User( // new User object:
rs.getLong(“id”), // id
rs.getString(“email”), // email
rs.getString(“password”), // password
rs.getString(“name”)); // name
});
}

RowMapper定义:

@FunctionalInterface
public interface RowMapper {
@Nullable
T mapRow(ResultSet var1, int var2) throws SQLException;
}

方法getUserByEmail中(ResultSet rs, int rowNum) -> {}语句实现一个RowMapper继承类的对象

queryForObject()方法中,传入SQL以及SQL参数后,JdbcTemplate会自动创建PreparedStatement,自动执行查询并返回ResultSet,我们提供的RowMapper需要做的事情就是把ResultSet的当前行映射成一个JavaBean并返回。整个过程中,使用ConnectionPreparedStatementResultSet都不需要我们手动管理。

RowMapper不一定返回JavaBean,实际上它可以返回任何Java对象。例如,使用SELECT COUNT(*)查询时,可以返回Long:

public long getUsers() {
return jdbcTemplate.queryForObject("SELECT COUNT() FROM users", null, (ResultSet rs, int rowNum) -> {
// SELECT COUNT(
)查询只有一列,取第一列数据:
return rs.getLong(1);
});
}

如果我们期望返回多行记录,而不是一行,可以用query()方法:

public List getUsers(int pageIndex) {
int limit = 100;
int offset = limit * (pageIndex - 1);
return jdbcTemplate.query(“SELECT * FROM users LIMIT ? OFFSET ?”, new Object[] { limit, offset },
new BeanPropertyRowMapper<>(User.class));
}

上述query()方法传入的参数仍然是SQL、SQL参数以及RowMapper实例。这里我们直接使用Spring提供的BeanPropertyRowMapper。如果数据库表的结构恰好和JavaBean的属性名称一致,那么BeanPropertyRowMapper就可以直接把一行记录按列名转换为JavaBean。

如果我们执行的不是查询,而是插入、更新和删除操作,那么需要使用update()方法:

public void updateUser(User user) {
// 传入SQL,SQL参数,返回更新的行数:
if (1 != jdbcTemplate.update(“UPDATE user SET name = ? WHERE id=?”, user.getName(), user.getId())) {
throw new RuntimeException(“User not found by id”);
}
}

只有一种INSERT操作比较特殊,那就是如果某一列是自增列(例如自增主键),通常,我们需要获取插入后的自增值。JdbcTemplate提供了一个KeyHolder来简化这一操作:

public User register(String email, String password, String name) {
// 创建一个KeyHolder:
KeyHolder holder = new GeneratedKeyHolder();
if (1 != jdbcTemplate.update(
// 参数1:PreparedStatementCreator
(conn) -> {
// 创建PreparedStatement时,必须指定RETURN_GENERATED_KEYS:
var ps = conn.prepareStatement(“INSERT INTO users(email,password,name) VALUES(?,?,?)”,
Statement.RETURN_GENERATED_KEYS);
ps.setObject(1, email);
ps.setObject(2, password);
ps.setObject(3, name);
return ps;
},
// 参数2:KeyHolder
holder)
) {
throw new RuntimeException(“Insert failed.”);
}
// 从KeyHolder中获取返回的自增值:
return new User(holder.getKey().longValue(), email, password, name);
}

JdbcTemplate还有许多重载方法,这里我们不一一介绍。需要强调的是,JdbcTemplate只是对JDBC操作的一个简单封装,它的目的是尽量减少手动编写try(resource) {...}的代码,对于查询,主要通过RowMapper实现了JDBC结果集到Java对象的转换。

我们总结一下JdbcTemplate的用法,那就是:

  • 针对简单查询,优选query()和queryForObject(),因为只需提供SQL语句、参数和RowMapper;
  • 针对更新操作,优选update(),因为只需提供SQL语句和参数;
  • 任何复杂的操作,最终也可以通过execute(ConnectionCallback)实现,因为拿到Connection就可以做任何JDBC操作。

实际上我们使用最多的仍然是各种查询。如果在设计表结构的时候,能够和JavaBean的属性一一对应,那么直接使用BeanPropertyRowMapper就很方便。如果表结构和JavaBean不一致怎么办?那就需要稍微改写一下查询,使结果集的结构和JavaBean保持一致。

例如,表的列名是office_address,而JavaBean属性是workAddress,就需要指定别名,改写查询如下:

SELECT id, email, office_address AS workAddress, name FROM users WHERE email = ?

RowMapper 的基本使用

使用过原生JDBC的朋友应该知道,从数据库查询出来的记录全都被保存在ResultSet结果集中,我们需要将结果集中的数据一条条地获取并设置到具体的实体类上,如此,该实体类才能在接下来的程序中使用。然而问题是,每次都要这么操作实在是太麻烦了,Spring就不应该提供什么功能来替我们做这些事情吗?答案当然是有的,那就是本文的主角——RowMapper

Spring JDBC中目前有两个主要的RowMapper实现,使用它们应该能解决大部分的场景了。

SingleColumnRowMapper

通过名字我们就能大概了解,在查询返回单列数据的时候,就该使用这个RowMapper,下面我们来看看具体的代码:

@Override
public String getStudentNameById(String id) {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

总结

对于面试,一定要有良好的心态,这位小伙伴面试美团的时候没有被前面阿里的面试影响到,发挥也很正常,也就能顺利拿下美团的offer。
小编还整理了大厂java程序员面试涉及到的绝大部分面试题及答案,希望能帮助到大家,

在这里插入图片描述

在这里插入图片描述

最后感谢大家的支持,希望小编整理的资料能够帮助到大家!也祝愿大家都能够升职加薪!

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

试题及答案**,希望能帮助到大家,

[外链图片转存中…(img-0f58zwNg-1712869239135)]

[外链图片转存中…(img-ziPi1U3A-1712869239135)]

最后感谢大家的支持,希望小编整理的资料能够帮助到大家!也祝愿大家都能够升职加薪!

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-3pimbzw7-1712869239136)]

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值