Spring基础:数据访问(1)

Spring JDBC通过模板和回调机制大大降低了使用JDBC的难度,以一种更直接,更间接的方式使API用户不用去关心资源获取,Statement创建,异常处理,资源释放等繁杂而乏味的工作,只需要去做那些必不可少的事。

以下例子都是基于Derby数据库的,这个数据库是JDK自带的,无需另外安装。

[size=large]1.Spring-Jdbc初步[/size]
在Maven Project中使用JdbcTemplate的话,除了在POM的依赖中加上JDBC Driver,还要使用DataSource,有很多开源的数据库连接池,比如Commons-Pool,C3P0,我们在第一步中使用了Spring自带的DriverManagerDataSource。

DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.apache.derby.jdbc.ClientDriver");
ds.setUrl("jdbc:derby://localhost:1527/sampledb_jdbc;create=true");

JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(ds);

String sql = "create table t_user("
+ "user_id int generated by default as identity"
+ ",user_name varchar(60)"
+ ",constraint PK_T_USER primary key(user_id))";
jdbcTemplate.execute(sql);

[color=red]****需要特别注意的是,在Derby中以编程的方式建立的数据库,默认是在APP这个Schema下面的[/color]

[size=large]2.Spring风格的JDBC[/size]
我们来配置一个Spring风格的Dao类
首先我们来写一个Respository类

@Repository
public class ForumDao {
@Autowired
private JdbcTemplate jdbcTemplate;

public void initDb() {
String sql = "create table t_user("
+ "user_id int generated by default as identity"
+ ",user_name varchar(60)"
+ ",constraint PK_T_USER primary key(user_id))";
jdbcTemplate.execute(sql);
}
}

[color=red]注意,需要使用@Repository自动注入[/color]
然后我们在xml中进行配置

<context:property-placeholder location="classpath:jdbc.properties"/>
<context:component-scan base-package="com.firethewhole.maventest09.dao"/>

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"/>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource"/>

这里我们使用类${}的占位符,所以还需要额外配置一个属性文件

jdbc.driverClassName=org.apache.derby.jdbc.ClientDriver
jdbc.url=jdbc:derby://localhost:1527/sampledb_jdbc;create=true

我们来写一段代码测试一下吧

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class ForumDaoTest {
@Autowired
private ForumDao forumDao;
@Test
public void testInitDb() {
forumDao.initDb();
}
}

然后打开derby控制台,发现已经这张t_user表已经被成功创建了。

[size=large]3.基本的数据库操作[/size]
[size=large]3.1 更改数据[/size]

public void addForum(Forum forum) {
String sql = "INSERT INTO t_forum(forum_name,forum_desc) VALUES(?,?)";
jdbcTemplate.update(sql, forum.getForumName(), forum.getForumDesc());
}

更新或者新增都是用update方法,这个时候PreparedStatement会根据参数类型猜测对应的数据库字段类型,当然我们也可以指定数据库字段类型。

public void addForum(Forum forum) {
final String sql = "INSERT INTO t_forum(forum_name,forum_desc) VALUES(?,?)";
Object[] params = new Object[] { forum.getForumName(), forum.getForumDesc() };
jdbcTemplate.update(sql, params, new int[] { Types.VARCHAR, Types.VARCHAR });
}

我们还可以使用PreparedStatementSetter回调方法

public void addForum(final Forum forum) {
String sql = "INSERT INTO t_forum(forum_name,forum_desc) VALUES(?,?)";
jdbcTemplate.update(sql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
// 这里需要forum为final
ps.setString(1, forum.getForumName());
ps.setString(2, forum.getForumDesc());
}
});
}

[color=red]注意,这里的参数是final修饰符[/color]
还有一种PreparedStatementCreator回调方法,可以手工创建PreparedStatement对象

public void addForum(final Forum forum) {
final String sql = "INSERT INTO t_forum(forum_name,forum_desc) VALUES(?,?)";
jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con)
throws SQLException {
// 这里需要sql为final
PreparedStatement ps = con.prepareStatement(sql);
ps.setString(1, forum.getForumName());
ps.setString(2, forum.getForumDesc());
return ps;
}
});
}

当然,我们在写入操作的时候,如果需要取得子增长主键的值,该怎么做呢?

DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.apache.derby.jdbc.ClientDriver");
ds.setUrl("jdbc:derby://localhost:1527/sampledb_jdbc");
final String sql = "insert into t_user1(user_name) values(?)";

JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(ds);

KeyHolder keyHolder = new GeneratedKeyHolder();

jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, "admin");
return ps;
}
}, keyHolder);
if (keyHolder.getKey() != null)
System.out.println(keyHolder.getKey().intValue());

[color=red]注意,一定要在创建PreparedStatement的时候加上Statement.RETURN_GENERATED_KEYS,否则即使加上KeyHolder也是不会获得子增长列的[/color]
复合主键的情况,可以使用getKeys,如果是新增多条记录,返回了多个主键,则需要使用getKeyList方法。

[size=large]3.2 批量更新数据[/size]
批量更新数据更加效率,也更加的节省数据库资源

public void addForums(final List<Forum> forums) {
final String sql = "INSERT INTO t_forum(forum_name,forum_desc) VALUES(?,?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
Forum forum = forums.get(i);
ps.setString(1, forum.getForumName());
ps.setString(2, forum.getForumDesc());
}
public int getBatchSize() {
return forums.size();
}
});
}

[color=red]注意:getBatchSize方法是整个集合的元素个数,而不是每一批次的元素个数[/color]

[size=large]3.3 查询[/size]
查询单条元素

public Forum getForum(final int forumId) {
String sql = "SELECT forum_name,forum_desc FROM t_forum WHERE forum_id=?";
final Forum forum = new Forum();
jdbcTemplate.query(sql, new Object[] { forumId }, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
forum.setForumName(rs.getString("forum_name"));
forum.setForumDesc(rs.getString("forum_desc"));
forum.setForumId(forumId);
}
});
return forum;
}

查询多条数据,使用List集合保存

public List<Forum> getForums(final int forumId, final int told) {
String sql = "SELECT forum_id,forum_name,forum_desc FROM t_forum WHERE forum_id BETWEEN ? AND ?";
final List<Forum> forums = new ArrayList<Forum>();
jdbcTemplate.query(sql, new Object[] { forumId, told }, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
Forum forum = new Forum();
forum.setForumId(rs.getInt("forum_id"));
forum.setForumName(rs.getString("forum_name"));
forum.setForumDesc(rs.getString("forum_desc"));
forums.add(forum);
}
});
return forums;
}

另外,查询多条数据还可以使用RowMapper<T>来进行

public List<Forum> getForumsRowMapper(final int forumId, final int told) {
String sql = "SELECT forum_id,forum_name,forum_desc FROM t_forum WHERE forum_id BETWEEN ? AND ?";
return jdbcTemplate.query(sql, new Object[] { forumId, told }, new RowMapper<Forum>() {
public Forum mapRow(ResultSet rs, int rowNum) throws SQLException {
Forum forum = new Forum();
forum.setForumId(rs.getInt("forum_id"));
forum.setForumName(rs.getString("forum_name"));
forum.setForumDesc(rs.getString("forum_desc"));
return forum;
}
});
}

RowMapper<T>和RowCallbackHandler相比,RowMapper是将集合整个返回,所以如果查询结果很大的话会很占内存。RowCallbackHandler可以一条条结果处理。
查询单值

public int getForumNum() {
String sql = "SELECT COUNT(*) FROM t_forum";
return jdbcTemplate.queryForInt(sql);
}

还有queryForXXX等一整套函数,主要是用来返回单值结果。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值