一. spring 的数据访问哲学
1. Srping 的目标之一就是允许我们在开发应用程序的时候, 能够遵循面向对象 (Object Oriented,OO) 原则中的 "针对接口式编程";
讲道理现在市面上的所有项目都离不开接口, 针对接口是面向对象的重要特征之一, 而 spring 的最终目标就是简化这一特征的使用方法(比如耦合).
只暴露接口, 从而体现出该实现的目标!
2. Spring 的 jdbc
Spring 为多种框架提供了 jdbc 支持;
Spring 本身就具有多个模块, 他是模块化的框架, 就好像 java 的虚拟机一样, 在什么地方, 用什么方法;
二. 配置数据源
1. 不管使用什么模板, 以及不管使用什么类型的数据库, 数据源是必须的(废话), 一般有三种方式;
通过 jdbc 驱动程序定义的数据源 (最基本的 jdbc, 注册驱动 -> 获取连接 ->获取操作对象 ->操作结果集 ->释放资源);
通过 JNDL 查找数据源(这个配置在服务器上面的比如 TOMCAT),(留待)
通过连接池获取数据源(c3p0,druid..)
三. jdbcTemplate (其实并不难, 我指的是里面源码, 很好理解, 前提是理解回调和递归原则)
1. 回调, 它最难理解的地方之一
类 A 调用类 B 的方法 B_1, 然后 A 继续去执行自己的东西, B_1 自己执行完了就告诉 A, 然后类 A 得到类 B_1 的执行结果, 去做些不可描述的事情
小王在打游戏, 叫小红去做饭, 小红做了一会饭做好了, 小王还在打游戏(这个时候小王可以决定吃饭, 也可以继续打游戏, 也可以边吃饭便打游戏(多线程异步))
这之中的实现步骤, 小王要实现一个接口, 小红做好了饭就调用这个接口去告诉小王传递饭好了的信息, 然而这个故事的主人公是小王, 小红只是个配角,
换句话说如果小王没有叫小红去做饭那么小红是什么我们都不知道, 小红之所以存在是因为小王他需要, 所以要小红属于小王, 小红对象依赖于小王对象而活,
在创建小王的同时就要将小红当做参数送给小王;
警察叔叔和贼的例子, 因为警察想要教育贼, 所以才有的贼回答问题(这个逻辑一点毛病都没有的)
- // 回调接口
- public interface CB {
- public void talk(String Word);
- }
- // 警察叔叔类
- public class Police implements CB {
- private Thief tf ; // 因为警察叔叔需要贼(要问问题), 所以才有的贼(贼要回答问题), 主业务需求, 诞生附属子实现!!!!
- public Police(Thief tf) {// 将贼对象传递给警察叔叔,
- this.tf = tf;
- }
- // 回调方法, 用来向当前类传递信息
- @Override
- public void talk(String Word) {
- System.out.println("毛贼说:" + Word);
- }
- /**
- * 警察发问
- */
- public void talkcrap() {
- System.out.println("你错了吗");
- tf.worng(this); // 警察进行询问, 这个贼是传入进来的, 将当前类对象传递个贼, 贼和警察是 1 对 1 关系(当然也可以实现 1 对多, 使用 LIist < 贼对象>)
- }
- }
- public class Thief {
- public void worng(CB cb) { // 回调接口传递信息
- cb.talk("警察叔叔我错了"); // 这个地方传递的参数, 当前行调用了接口的 talk 方法
- }
- }
回调很绕, 我解释不太清除, 这个例子是自己想出来的, 网上有很多大佬的解释, 多敲几遍, 多调试几遍代码, 就能看懂里面的逻辑所在了;
附上我修改过 1 对多例子
- // 回调接口
- public interface CallBack {
- // 这里才是回调, 要调用这个方法, 才能够传递信息!!!!!
- public void tellAnswer(int answer);
- }
- // 学生接口
- public interface Student {
- // 这里放入一个对象, 要放入主要对象, 就是当前类依赖于的对象: 主业务对象
- public void resovleQuestion(CallBack callBack);
- }
- // 老师类
- public class Teacher implements CallBack {
- private List<Student> stus;//tom,lusy,rose
- public Teacher(List<Student> stus) {
- System.out.println("teacher 对象创建, 学生数量"+stus.size());
- this.stus = stus;
- }
- @Override
- public void tellAnswer(int answer) {
- System.out.println(Thread.currentThread().getName()+"你的答案是" + answer);
- }
- public void askQuestion() {
- Thread.currentThread().setName("老师线程");
- System.out.println("你们写了多长时间的作业?");
- long end = 0;
- // 启动所有线程, 回答所有问题
- for (int i = 0; i <stus.size(); i++) {
- new Thread((Runnable) stus.get(i)).start();
- }
- try {
- System.out.println(Thread.currentThread().getName()+"开始休眠 15 秒, 等待所有人的问题回答完");
- Thread.sleep(15000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("老师线程休眠结束"+System.currentTimeMillis());
- for (int i = 0; i < stus.size(); i++) {
- stus.get(i).resovleQuestion(this);
- }
- // 当前类实现了回调接口, 所以讲当前类对象传入进去
- //
- // long end = System.currentTimeMillis();
- // System.out.println("回答问题的时间:"+(end-start));
- }
- }
学生的实现类只需继承 Student 接口即可, 其中使用了线程暂停来模拟思考题目的过程; sleep();
2.org.springframework.jdbc.core.RowMapper
将 ResultSet 封装成 javaBeen 类型, 他是一个函数式接口
- // 将 ResultSet 封装成 User 类型
- public class RowMapperDemo implements RowMapper<User>{
- @Override
- 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;
- }
- }
3. 方法总结
execute 方法: 可以用于执行任何 SQL 语句, 一般用于执行 update,delete,insert
update 方法及 batchUpdate 方法: update 方法用于执行新增, 修改, 删除等语句; batchUpdate 方法用于执行批处理相关语句;
call 方法: 用于执行存储过程, 函数相关语句.
4. 最重要的还是回调
- //RowMapper 接口将 ResultSet 里面的数据映射到 JavaBeen 中 ->封装数据
- //queryforList(), 执行 SQL, 将数据返回为 List<Map<String(列名),Object(行数据))>>
- List<User> list = jt.query(sql, (rs ,i)-> new User(rs.getString("name"),rs.getString("password")));
- new RowMapper<User>() {
- @Override
- public User mapRow(ResultSet resultSet, int i) throws SQLException {
- User user = new User();
- user.setPassword(resultSet.getString("password"));
- user.setName(resultSet.getString("name"));
- return user;
- }
- };
5. public <T> List<T> query(String sql, RowMapper<T> rowMapper): 将执行的结果通过 rowMapper 接口映射为实体类对象
- public List<Student> getStudents() {
- return jt.query("select * from student", (rs, i) -> new Student(rs.getString("stuno"),
- rs.getString("name"), rs.getInt("age"), rs.getString("address")));
- // 该 lambda 表达式相当于
- new RowMapper<Student>() {
- @Override
- public Student mapRow(ResultSet resultSet, int i) throws SQLException {
- return new Student(rs.getString("stuno"),rs.getString("name"), rs.getInt("age"), rs.getString("address"));
- }
- };
- }
- 6.public <T> T queryForObject(String sql, Class<T> requiredType)
一般用于单列查询, 主要用来执行返回聚合函数的查询
- jt.queryForObject("select count(*) from student where stuno ='"+stuno+"'", Integer.class);
- // 返回 Integer 类型
- 7.int update(String sql, @Nullable Object... args)
一般用来执行 更新, 删除, 增加操作, 通配符使用方式如下
- return jt.update("insert into student values(?,?,?,?)", new Object[]{
- stu.getStuno(), stu.getName(), stu.getAge(), stu.getAddress()
- });
- // 这两种方式是一样的效果, 简略了代码
- return jt.update("insert into student values(?,?,?,?)",stu.getStuno(), stu.getName(), stu.getAge(), stu.getAddress());
8. execute 语句, 这种语句一般用来创建数据库, 创建表那些, 用于那种不复杂 (不使用通配符 "?") 的语句
jt.execute("DELETE FROM student WHERE stuno ='" + stuno + "'");
不好使用通配符, 这条 sql 半写死的, 主要还是用来执行创建数据库, 创建表的语句
jt.update("DELETE FROM student WHERE stuno = ?",stuno);
这两条语句的作用是一样的
9. 我 uml 图画的不是很好, 是在网上边学边画的, 有问题请不吝赐教 QAQ
来源: https://www.cnblogs.com/lyuweigh/p/2eb723cc7b546f7b5a8e1a1908d13535.html