Java-连接池(数据库元数据、JDBCTemplate)

1. 数据库元数据

1.1 元数据概念

  • 数据库、表、列的定义信息。

1.2 ParameterMetaData参数元数据

  • ParameterMetaData 用于获取有关PreparedStatement 对象中每个参数标记的类型和属性。

1.2.1 获取ParameterMetaData对象

  • 通过PreparedStatementgetParameterMetaData()方法获取

1.2.2 ParameterMetaData常用方法

- int getParameterCount();  获取PreparedStatement的SQL语句中?的个数
- int getParameterType(int param);  获取指定参数的SQL类型

注意:不是所有的数据库驱动都能获取参数类型(MySQL会出异常)

1.3 ResultSetMetaData结果集元数据

  • ResultSetMetaData 用于获取有关ResultSet 对象中列的类型和属性的信息。

1.3.1 获取ResultSetMetaData对象

  • 通过ResultSetgetMetaData() 方法来获取

1.3.2 ResultSetMetaData常用方法

- int getColumnCount();  返回此 ResultSet对象中的列数
- String getColumnName(int column);  获取指定列的名称
- String getColumnTypeName(int column);  获取指定列的数据库特定类型名称

1.4 示例案例(自定义框架)

需求:使用连接池工具类实现数据库的增删改,该工具类都只需要传入SQL与参数即可

目的:使用元数据将增删改的方法合并成一个

import Utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Notes01 {
    //实现直接传入sql语句和参数
    public static void update(String sql,Object...args) throws SQLException {
        //获取一个连接
        Connection connection = JdbcUtils.getConnection();
        //获取一个预编译对象
        PreparedStatement pst = connection.prepareStatement(sql);
        //获得元数据对象
        ParameterMetaData pmd = pst.getParameterMetaData();
        //获取当前sql语句需要设置的参数个数
        int count = pmd.getParameterCount();
        //设置参数
        for (int i = 0; i < count; i++) {
            pst.setObject(i+1,args[i]);
        }
        //执行SQL语句
        pst.executeUpdate();
        //关闭资源
        JdbcUtils.close(null,pst,connection);
    }
}

2. JdbcTemplate(重点)

2.1 JdbcTemplate概念

  • JdbcTemplate就是Spring对JDBC的封装,目的是使JDBC更加易于使用

  • 分类:

    1. execute :可以执行所有SQL语句,一般用于执行DDL语句。
    2. update :用于执行INSERT 、UPDATE 、DELETE 等DML语句。
    3. queryXxx :用于DQL数据查询语句。
  • 优点:
    1. 不需要自行管理连接;
    2. 不需要自行设置参数;
    3. 查询时可直接返回对应的实体类。

2.2 JdbcTemplate的创建和使用

2.2.1 使用DRUID实现连接池

2.2.1.1 常用方法
- public JdbcTemplate(DataSource dataSource);   创建JdbcTemplate对象
- public void execute(final String sql);    execute可以执行所有SQL语句,一般是执行DDL语句
2.2.1.2 使用步骤
  1. 创建DruidDataSource连接池;
  2. 导入JdbcTemplate的jar包;
  3. 创建JdbcTemplate 对象,传入Druid 连接池;
  4. 调用executeupdatequeryXxx 等方法。
public class Demo01 {
    public static void main(String[] args) {
        //创建的JdbcTemplate对象
        JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());
        // 创建表的SQL语句
        String sql = "CREATE TABLE CAR(cid INT PRIMARY KEY AUTO_INCREMENT,cname VARCHAR(20),price DOUBLE);";
        jdbcTemplate.execute(sql);
    }
}

2.3 JdbcTemplate实现增删改

2.3.1 常用方法

- public void update(final String sql); update用于执行DML语句

2.3.2 使用步骤

  1. 创建JdbcTemplate对象

  2. 编写SQL语句

  3. 使用JdbcTemplate对象的update方法进行增删改

2.3.3 增删改示例案例

需求:使用JdbcTemplate实现增删改

import Utils.JdbcUtils;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;

public class Notes02 {
    //创建JdbcTemplate对象
    JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());

    /*创建表*/
    @Test
    public void createTable(){
        //编写SQL语句
        String sql1 = "create table if not EXISTS person(id int primary key auto_increment, name varchar(10),age int);";
        jdbcTemplate.execute(sql1);
    }

    /*增加数据*/
    @Test
    public void add() {
        String sql = "insert into person(name,age) values (?,?);";
        jdbcTemplate.update(sql,"小明",18);
        jdbcTemplate.update(sql,"小黄",20);
    }

    /*删除数据*/
    @Test
    public void delete() {
        String sql = "DELETE FROM person WHERE id = ?;";
        jdbcTemplate.update(sql,2);
    }

    /*更新数据*/
    @Test
    public void update() {
        String sql = "UPDATE person SET age = ? WHERE name = ?;";
        jdbcTemplate.update(sql,20,"小明");
    }
}

2.4 JdbcTemplate实现查询

2.4.1 返回值为int和long

- public int queryForInt(String sql);   执行查询语句,返回一个int类型的值。
- public long queryForLong(String sql); 执行查询语句,返回一个long类型的值。

2.4.2 使用queryForObject查询单个字段/单个实体类对象

- public <T> T queryForObject(String sql, Class<T> requiredType);   执行查询语句,返回一个指定类型的数据。
比如返回类型指定为String:
String str = JdbcTemplate对象名.queryForObject(sql, String.class);

注意事项

  1. 使用queryForObject查询时,如果没有结果会报错

  2. queryForObject返回的可以是单个字段,也可以是单个实体类对象(使用BeanPropertyRowMapper类)

2.4.3 使用queryForMap查询单个对象,返回一个Map对象

- public Map<String, Object> queryForMap(String sql);   执行查询语句,将一条记录放到一个Map对象中。

注意事项

1. 一个Map对象就存储一条记录,key存储的是列名,value存储的是该记录中该列对应的值

2. 返回的不是实体类对象,仅仅是一个Map对象

2.4.4 使用queryForList查询返回多个对象

- public List<Map<String, Object>> queryForList(String sql);
    执行查询语句,返回一个List集合,List中存放的是Map类型的数据。

2.4.5 query使用自定义RowMapper实现一次性查询多个实体类对象(多用于多表查询)

- public <T> List<T> query(String sql, RowMapper<T> rowMapper);
    执行查询语句,返回一个List集合,List中存放的是RowMapper指定类型的数据
RowMapper<T>接口的重写方法中的resultSet:jdbctemplate每得到一行数据,都会直接给了resultSet对象,而i为当前是第几行

获取RowMapper,可通过结果集获取相应的值赋予给指定类对象的参数(详见案例)

使用步骤

  1. 定义一个类
  2. 创建JdbcTemplate对象
  3. 编写查询的SQL语句
  4. 使用JdbcTemplate对象的query方法,并传入RowMapper匿名内部类
  5. 在匿名内部类中将结果集中的一行记录转成一个指定类型对象

2.4.6 query使用BeanPropertyRowMapper实现一次性查询多个实体类对象(只能单表查询)

- public <T> List<T> query(String sql, new BeanPropertyRowMapper<T> (Class<T> requiredType));
    执行查询语句,返回一个List集合,List中存储的是实体类对象,其中BeanPropertyRowMapper类实现了RowMapper接口。
- public class BeanPropertyRowMapper<T> implements RowMapper<T>
    BeanPropertyRowMapper类实现了RowMapper接口

2.4.7 示例代码

import Utils.JdbcUtils;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class Notes03 {

    JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());

    /*
    queryForObject查询单个字段
     */
    @Test
    public void test01() {
        String sql = "SELECT COUNT(id) FROM student";
        //使用JdbcTemplate对象调用queryForObject
        int count = jdbcTemplate.queryForObject(sql, int.class);
        System.out.println("总人数:" + count);
    }

    /*
    queryForObject查询单个实体类对象
    */
    @Test
    public void test02() {
        String sql = "SELECT * FROM student WHERE id = ?";
        //使用JdbcTemplate对象调用queryForObject,传入BeanPropertyRowMapper对象
        Student student = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Student.class),3);
        System.out.println("查询结果:" + student);
    }

    /*
    queryForMap查询单个实体类对象,返回Map对象
    */
    @Test
    public void test03() {
        String sql = "SELECT * FROM student WHERE id = ?";
        //使用JdbcTemplate对象调用queryForMap
        Map<String, Object> som = jdbcTemplate.queryForMap(sql,2);
        System.out.println("查询结果:" + som);
    }

    /*
    queryForList查询多个实体类对象,返回Map对象组成的List集合
    */
    @Test
    public void test04() {
        String sql = "SELECT * FROM student";
        //使用JdbcTemplate对象调用queryForList,返回的是List集合,里面存储Map
        List<Map<String, Object>> mapList = jdbcTemplate.queryForList(sql);
        System.out.println("查询结果:" + mapList);
    }

    /*
    query查询多个实体类对象,使用自定义rowMapper
    */
    @Test
    public void test05() {
        String sql = "SELECT * FROM student";
        //使用JdbcTemplate对象调用query,使用匿名内部类定义rowMapper
        List<Student> list = jdbcTemplate.query(sql, new RowMapper<Student>() {
            /*
            mapRow(ResultSet resultSet, int i)
                    resultSet : jdbctemplate 每得到一行数据,都会直接给了resultSet对象。
                    i:当前是第几行。
            */

            @Override
            public Student mapRow(ResultSet resultSet, int i) throws SQLException {
                Student student = new Student();
                student.setId(resultSet.getInt("id"));
                student.setName(resultSet.getString("name"));
                student.setAge(resultSet.getInt("age"));
                return student;
            }
        });
        System.out.println("查询结果:" + list);
    }

    /*
    query查询多个实体类对象,返回list集合,里面存放实体类对象
    */
    @Test
    public void test06() {
        String sql = "SELECT * FROM student";
        List<Student> list = jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(Student.class));
        System.out.println("查询的学生:"+ list);
    }
}

3. 数据库三层架构(重点)

3.1 分层的作用

  1. 解耦:降低层与层之间的耦合性;
  2. 可维护性:提高软件的可维护性,对现有功能进行修改和更新时不会影响原有的功能;
  3. 可扩展性:提高软件的可扩展性,添加新的功能时不影响到现有的功能;
  4. 可重用性:不同层之间进行功能调用时,相同的功能可重复使用。

也有缺点:

  1. 工作量增加,需要分包,编写很多类
  2. 代码复杂程度提升

3.2 分层的方式

  • 按照包进行划分
    • view表示层-service业务逻辑层-dao数据访问层
    • 其他的包就不分层,如工具类,实体类,测试类

3.3 使用分层实现用户登录和注册功能

  • 前提:数据库中必须要先把用户表创建出来才能执行

  • 准备工作:先创建资源(用户类)

public class User {
    private int id;
    private String name;
    private String password;

    public User() {
    }

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public User(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
    /*此处省略getter&setter,实际是有的*/
}
  • 然后是提供一个工具类,用来创建连接池,获取连接,关闭连接等操作
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {

    private static DataSource dataSource ;

    /*  配置文件的加载只需加载一次,使用静态代码块*/
    static{
        try {
            Properties properties =new Properties();
            InputStream inputStream =  JdbcUtils.class.getResourceAsStream("/druid.properties");
            properties.load(inputStream);

            //创建druid的连接池
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接池
    public static DataSource getDataSource(){
        return dataSource;
    }

    //获取连接
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    //关闭资源
    public static void close(ResultSet rs , Statement st , Connection conn){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • view层代码
package com.java.view;

import com.java.User.User;
import com.java.service.UserService;

import java.util.Scanner;

public class AppAdmin {
    private static Scanner sc = new Scanner(System.in);
    //创建service对象
    private static UserService userService = new UserService();

    public static void main(String[] args) {
        System.out.println("请选择功能:1(注册) 2(登录)");
        String option = sc.nextLine();
        if ("1".equalsIgnoreCase(option)) {
            reg();
        } else if ("2".equalsIgnoreCase(option)) {
            login();
        } else {
            System.out.println("你的选择有误");
        }
    }

    /*
    登录功能
     */
    private static void login() {
        System.out.println("请输入用户信息");
        System.out.println("用户名:");
        String name = sc.nextLine();
        System.out.println("密码:");
        String password = sc.nextLine();
        //封装到User对象中发送
        User user = new User(name,password);
        if (userService.login(user)) {
            System.out.println("欢迎" + user.getName() + "登录成功");
        }else {
            System.out.println("用户名或者密码有误!!!");
        }
    }

    /*
    注册功能
     */
    private static void reg() {
        String name = null;
        while (true) {
            System.out.println("请输入用户注册信息");
            System.out.println("用户名:");
            name = sc.nextLine();
            //向service层发送name,如果返回的是false,说明用户名不存在,否则用户名已被注册
            if (!userService.isExist(name)) {
                //取反后判断是true,说明用户名不存在,可以注册,跳出循环
                break;
            } else {
                System.out.println("用户名已注册!");
            }
        }
        System.out.println("密码:");
        String password = sc.nextLine();
        //把数据封装到User对象
        User user = new User(name,password);
        //将数据传输给数据库
        userService.saveUser(user);
        System.out.println("您已注册成功");
    }
}
  • service层代码
package com.java.service;

import com.java.User.User;
import com.java.dao.UserDao;

public class UserService {

    private UserDao userDao = new UserDao();

    //检查用户名是否存在
    public boolean isExist(String name) {
        //根据用户名查询用户,如果返回null则不存在,返回有用户则存在用户
        User user = userDao.findUserName(name);
        return user != null;
        /*
        逻辑是向数据库发送查询信息,返回有用户,则往view层返回true;如果返回null,则往view层返回false
         */
    }

    //保存用户信息
    public void saveUser(User user) {
        userDao.addUser(user);
    }

    //登录功能
    public boolean login(User user) {
        User targetUser = userDao.login(user);
        return targetUser != null;
        /*
        逻辑:向数据库发送注册信息,返回有用户,则向view层返回true;若返回null,则往view层返回false
         */
    }
}
  • dao层代码
package com.java.dao;

import Utils.JdbcUtils;
import com.java.User.User;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class UserDao {
    JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());

    public User findUserName(String name) {
        //如果根据用户名查询到用户,则返回用户,如果报错,则返回null
        //注意queryForObject方法如果查询不到用户不会返回null,而是直接报错
        try {
            String sql = "SELECT * FROM user WHERE name = ?;";
            User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), name);
            return user;
        } catch (DataAccessException e) {
            return null;
        }
    }

    public void addUser(User user) {
        String sql = "insert into user(name,password) values (?,?);";
        jdbcTemplate.update(sql,user.getName(),user.getPassword());
    }

    public User login(User user) {
        //根据传入的user的信息查询用户,存在返回用户,不存在报错,此时catch后返回null
        try {
            String sql = "select * from user where name = ? and password = ?;";
            User targetUser = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), user.getName(), user.getPassword());
            return targetUser;
        } catch (DataAccessException e) {
            return null;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/KeepStruggling/article/details/82143862
今日推荐