내 연락처 정보
우편메소피아@프로톤메일.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
JavaWeb(1:기본지식 및 환경구축)https://blog.csdn.net/xpy2428507302/article/details/140365130?spm=1001.2014.3001.5501JavaWeb(2: 서블릿과 Jsp, 리스너와 필터)
https://blog.csdn.net/xpy2428507302/article/details/140365159?spm=1001.2014.3001.5501
목차
JDBC(Java DataBase Connectivity): 자바 데이터베이스 연결 기술
JDBC는 특정 데이터베이스와 독립적인 관리 시스템이자 범용 SQL 데이터베이스 액세스 및 운영을 위한 공용 인터페이스입니다.
다양한 데이터베이스에 액세스할 수 있는 통합된 방법을 제공하기 위한 일련의 표준을 정의합니다.
JDBC는 Java 언어로 작성된 클래스와 인터페이스 집합으로 구성된 데이터베이스 액세스를 위한 애플리케이션 API입니다.
두 가지 레벨이 포함됩니다:
① 프로그래머가 호출할 수 있는 애플리케이션 중심의 API입니다.
② 제조업체가 데이터베이스 드라이버를 개발할 수 있는 데이터베이스 지향 API입니다.
서로 다른 데이터베이스를 JDBC로 연결하려면 서로 다른 데이터베이스 드라이버 패키지만 로드하면 되며, 데이터베이스 운영 언어의 차이에 대해 걱정할 필요가 없습니다.
① 데이터베이스 테이블 생성
- CREATE TABLE users(
- id INT PRIMARY KEY,
- `name` VARCHAR(40),
- `password` VARCHAR(40),
- email VARCHAR(60),
- birthday DATE
- );
- INSERT INTO users(id,`name`,`password`,email,birthday)
- INSERT INTO users(id,`name`,`password`,email,birthday)
- INSERT INTO users(id,`name`,`password`,email,birthday)
- SELECT * FROM users;
② 데이터베이스 종속성 가져오기
- <!--mysql的驱动-->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>8.0.31</version>
- </dependency>
③ JDBC 사용:
Ⅰ. 데이터베이스 드라이버 로드(Java 프로그램과 데이터베이스 간의 브리지)
Ⅱ. 자바 프로그램과 데이터베이스 간의 연결인 Obtain Connection
Ⅲ. Connection에 의해 생성된 SQL을 데이터베이스로 보내는 State 객체를 생성합니다.
IV.SQL 문 작성(업무에 따라 다른 SQL 작성)
Ⅴ. SQL을 실행한다. (반환값을 받고 싶다면 ResultSet 객체를 생성하여 SQL문 실행 후 쿼리 결과를 저장한다.)
Ⅵ. 연결을 닫습니다.
- public class JDBCTest {
- public static void main(String[] args) throws Exception {
- //配置信息
- //要连接的数据库URL(解决中文乱码问题:useUnicode=true&characterEncoding=utf8&useSSL=true)
- String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
- //连接的数据库时使用的用户名
- String username = "root";
- //连接的数据库时使用的密码
- String password = "123456";
- Connection conn = null;
- Statement st = null;
- ResultSet rs = null;
-
- try {
- //1.加载驱动
- Class.forName("com.mysql.cj.jdbc.Driver");
- //2.获取与数据库的链接
- conn = DriverManager.getConnection(url, username, password);
- //3.获取用于向数据库发送sql语句的statement对象
- st = conn.createStatement();
- String sql = "select id,name,password,email,birthday from users";
- //4.向数据库发sql,并获取代表结果集的resultset对象
- //查:executeQuery 增删改:executeUpdate
- rs = st.executeQuery(sql);
- //5.取出结果集的数据
- while (rs.next()) {
- System.out.println("id=" + rs.getInt("id"));
- System.out.println("name=" + rs.getString("name"));
- System.out.println("password=" + rs.getString("password"));
- System.out.println("email=" + rs.getString("email"));
- System.out.println("birthday=" + rs.getDate("birthday"));
- }
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- try {
- //6.关闭链接,释放资源(先开后关)
- rs.close();
- st.close();
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- }
jdbc:mysql://localhost:3306/xxx
제이디비씨 | 규약 |
마이스클 | 하위 프로토콜 |
로컬호스트:3306 | 호스트:포트 |
트리플 엑스 | 데이터 베이스 |
일반적으로 사용되는 데이터베이스 URL 주소를 작성하는 방법:
Connection은 데이터베이스 링크를 표현하는데 사용되며, Collection은 데이터베이스 프로그래밍에서 가장 중요한 객체입니다.
클라이언트와 데이터베이스 간의 모든 상호 작용은 연결 개체를 통해 완료됩니다., 이 개체의 일반적인 메서드:
createStatement() | SQL을 데이터베이스로 보내는 명령문 개체를 만듭니다. |
SQL문 준비(SQL) | 미리 컴파일된 SQL을 데이터베이스로 보내는 prepareSatement 개체를 만듭니다. |
setAutoCommit(boolean 자동커밋) | 트랜잭션이 자동으로 커밋되는지 여부 설정 |
저지르다() | 링크로 거래 제출 |
롤백() | 이 링크에서 트랜잭션 롤백 |
명령문 개체는 SQL 문을 데이터베이스로 보내는 데 사용됩니다. 명령문 개체의 일반적인 방법은 다음과 같습니다.
executeQuery(문자열 sql) | 쿼리 문을 데이터에 보내고 ResultSet 결과 집합을 반환하는 데 사용됩니다. |
executeUpdate(문자열 sql) | 삽입, 업데이트 또는 삭제 문을 데이터베이스로 보내고 데이터베이스에서 업데이트된 행 수를 반환하는 데 사용됩니다. |
실행(문자열 sql) | 임의의 SQL 문을 데이터베이스로 보내는 데 사용됩니다. |
addBatch(문자열 sql) | 여러 SQL 문을 일괄 처리에 넣기 |
실행배치() | 실행을 위해 일괄 SQL 문을 데이터베이스로 보냅니다. |
알아채다:
작업 효율성을 높이기 위해 일반적으로 실행 메서드를 직접 사용하지 않습니다.
대신에 쿼리에 대해 ExecuteQuery를 사용하고, 추가, 삭제 및 수정에 대해 ExecuteUpdate를 사용하십시오.
ResultSet은 SQL 문의 실행 결과를 나타내는 데 사용됩니다.
결과 집합 실행 결과 집합을 캡슐화할 때 다음을 사용합니다.테이블과 비슷방법。
ResultSet 객체는 테이블 데이터 행을 가리키는 커서를 유지합니다.처음에는 커서가 첫 번째 행 앞에 있습니다.。
ResultSet.next() 메서드를 호출하면 커서가 특정 데이터 행을 가리키도록 할 수 있고, getxxx 메서드를 호출하여 해당 행의 데이터를 가져올 수 있습니다.
① 모든 유형의 데이터 가져오기
getObject(int 인덱스) | 지정된 열 수에 따라 Object 개체를 가져옵니다. |
getObject(문자열 열 이름) | 지정된 속성 이름에 따라 Object 객체를 가져옵니다. |
② 문자열 유형 가져오기 등 지정된 유형의 데이터를 가져옵니다.
getString(인덱스) | 지정된 열 수를 기반으로 String 객체를 가져옵니다. |
getString(문자열 열 이름) | 지정된 속성 이름에 따라 String 객체를 가져옵니다. |
③ 결과 세트를 스크롤하는 방법:
다음() | 다음 줄로 이동 |
이전의() | 이전 줄로 이동 |
절대(int 행) | 지정된 줄로 이동 |
beforeFirst() | resultSet 앞으로 이동 |
afterLast() | resultSet의 끝으로 이동 |
Jdbc 프로그램 실행이 완료된 후 실행 프로세스 중에 데이터베이스와 상호 작용하기 위해 프로그램에서 생성된 개체를 해제해야 합니다.
이러한 객체는 일반적으로 ResultSet, State 및 Connection 객체입니다.
특히 Connection 개체는 매우 희귀한 리소스이므로 사용 후 즉시 해제해야 합니다.
연결을 신속하고 정확하게 종료할 수 없으면 쉽게 시스템 다운타임이 발생할 수 있습니다.
리소스 릴리스 코드가 실행될 수 있도록 하려면 리소스 릴리스 코드도 finally 문에 배치되어야 합니다.
개발을 위해 State를 사용할 때 두 가지 문제가 있습니다.
① 문자열을 자주 이어붙여야 하고 오류율이 높다.
- String username = "zhangsan";
- String password = "123";
- String sql = "select * from users where name='"+username+"' and password='"+password+"'";
- ResultSet rs = st.executeQuery(sql);
② SQL 인젝션의 위험성이 있다
SQL 삽입: 사용자가 입력한 데이터에 불법적인 SQL 문을 삽입하는 영리한 기술을 사용하여 문자열을 연결하고 SQL 단락을 유발하여 데이터베이스 데이터를 훔치는 행위입니다.
- public class SQLTest {
- public static void main(String[] args) throws Exception {
- // 正常登陆sql:select * from users where name='张三' and password ='123456'
- //login("张三","123456");
- // SQL 注入:select * from users where name='' and password ='123456' or '1'='1'
- login("", "123456' or '1'='1");
- }
-
- public static void login(String username, String password) throws Exception {
- String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
- String dbUsername = "root";
- String dbPassword = "123456";
-
- //1.加载驱动
- Class.forName("com.mysql.cj.jdbc.Driver");
- //2.获取与数据库的链接
- Connection conn = DriverManager.getConnection(url, dbUsername, dbPassword);
- //3.获取用于向数据库发送sql语句的statement
- Statement st = conn.createStatement();
- String sql = "select * from users where name='" + username + "' and password='" + password + "'";
- System.out.println(sql);
- //4.向数据库发sql,并获取代表结果集的rs
- ResultSet rs = st.executeQuery(sql);
- if (rs.next()) {
- System.out.println("登录成功");
- } else {
- System.out.println("登录失败");
- }
- //6.关闭链接,释放资源
- rs.close();
- st.close();
- conn.close();
- }
- }
작업 결과:
알아채다:
SQL 문에서 및의 우선 순위는 or보다 크므로 실행된 SQL은 '1' = '1'인 사용자로부터 *를 선택하는 것과 동일합니다.
해결책:SQL 자리표시자 기능을 제공하는 SQL문의 하위 클래스인 ReadyStatement를 사용하세요.
문자열을 이어붙일 필요가 없으며 사용자가 입력한 데이터가 완전히 감지되므로 더욱 안전합니다.
- public class PSTest {
- public static void main(String[] args) throws Exception {
- // 正常登陆sql:select * from users where name='张三' and password ='123456'
- //login("张三","123456");
- // SQL 注入:select * from users where name='' and password ='123456' or '1'='1'
- login("", "123456' or '1'='1");
- }
-
- public static void login(String username, String password) throws Exception {
- String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
- String dbUsername = "root";
- String dbPassword = "123456";
-
- Class.forName("com.mysql.cj.jdbc.Driver");
-
- Connection conn = DriverManager.getConnection(url, dbUsername, dbPassword);
- //获取用于向数据库发送预编译sql语句的prepareStatement
- String sql = "select * from users where name = ? and password = ?";
- System.out.println(sql);
- PreparedStatement ps = conn.prepareStatement(sql);
- //给占位符 ? 填充数据
- ps.setString(1, username);
- ps.setString(2, password);
- ResultSet rs = ps.executeQuery();
- if (rs.next()) {
- System.out.println("登录成功");
- } else {
- System.out.println("登录失败");
- }
- rs.close();
- ps.close();
- conn.close();
- }
- }
작업 결과:
트랜잭션은 모두 성공하거나 모두 실패하는 논리적 작업 집합을 나타냅니다(ACID 원칙).
Jdbc 프로그램이 데이터베이스에서 Connection 개체를 얻으면 기본적으로 Connection 개체는 자동으로 데이터베이스에 트랜잭션을 제출합니다.
이 기본 제출 방법을 끄고 하나의 트랜잭션에서 여러 SQL이 실행되도록 하려면 다음 JDBC 제어 트랜잭션 문을 사용할 수 있습니다.
연결.setAutoCommit(false); | 거래 시작 |
연결.롤백(); | 롤백 트랜잭션 |
연결.commit(); | 트랜잭션 커밋 |
① 계정 테이블 생성
- /*创建账户表*/
- CREATE TABLE account(
- id INT PRIMARY KEY AUTO_INCREMENT,
- NAME VARCHAR(40),
- money DECIMAL(9,2)
- );
- /*插入测试数据*/
- insert into account(name,money) values('A',1000);
- insert into account(name,money) values('B',1000);
- insert into account(name,money) values('C',1000);
② 양도 성공 시 사업 시나리오를 시뮬레이션
- //失败后让数据库自动回滚事务
- public class Demo {
- public static void main(String[] args) {
- String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
- String username = "root";
- String password = "123456";
- Connection conn = null;
- try {
- Class.forName("com.mysql.cj.jdbc.Driver");
- conn = DriverManager.getConnection(url, username, password);
-
- //通知数据库开启事务,false表示开启
- conn.setAutoCommit(false);
-
- String sql1 = "update account set money=money-100 where name = 'A' ";
- conn.prepareStatement(sql1).executeUpdate();
-
- //模拟执行完SQL1之后程序出现了异常而导致后面的SQL无法正常执行,事务也无法正常提交
- int x = 1/0;
-
- String sql2 = "update account set money=money+100 where name = 'B' ";
- conn.prepareStatement(sql2)executeUpdate();
-
- //sql1 和 sql2都顺利执行,就提交事务
- conn.commit();
- System.out.println("成功!!!");
- } catch (Exception e) {
- //出现异常,通知数据库回滚事务
- conn.rollback();
- e.printStackTrace();
- } finally {
- conn.close();
- }
- }
- }
다양한 요청에서는 매번 데이터베이스에 연결하고 리소스를 해제해야 하며, 반복되는 코드가 많이 작성됩니다.
데이터베이스 연결 준비 및 해제 작업을 도구 클래스로 캡슐화하고, 반복되는 코드 작성을 피하기 위해 이를 사용할 때 직접 호출합니다.
- public class JdbcUtil {
- private static Connection connection;
- private static String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
- private static String username = "root";
- private static String password = "123456";
-
- //驱动(类)只需要加载一次,放静态代码块即可
- static {
- try {
- //加载数据库驱动
- Class.forName("com.mysql.cj.jdbc.Driver");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- // 获取数据库连接对象
- public static Connection getConnection() throws SQLException {
- return DriverManager.getConnection(url, username, password);
- }
-
- // 释放资源(利用多态:Statement 和 PreparedStatement 都可以传进来)
- public static void release(Connection conn, Statement st, ResultSet rs) {
- try {
- if (rs != null) {
- rs.close();
- }
- if (st != null) {
- st.close();
- }
- if (conn != null) {
- conn.close();
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
다음과 같은 콜 케이스: 사용자 추가
-
- public void add(String name, String password) {
- Connection conn = null;
- PreparedStatement ps = null;
- try {
- conn = JdbcUtil.getConnection();
- String sql = "insert into users(name,password) values(?,?)";
- ps = conn.prepareStatement(sql);
- ps.setString(1, name);
- ps.setString(2, password);
- ps.executeUpdate();
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- JdbcUtil.release(conn, ps, null);
- }
- }
JDBC 개발 프로세스:
Ⅰ. 데이터베이스 드라이버 로드(한 번만 로드하면 됨)
Ⅱ. 데이터베이스 연결(Connection)을 설정합니다.
Ⅲ. Connection에 의해 생성된 SQL을 데이터베이스로 보내는 State 객체를 생성합니다.
Ⅴ.SQL문 작성
Ⅴ. SQL 실행(Query-->ResultSet 객체가 결과 세트를 수신함)
Ⅵ. 연결을 종료하고 리소스를 해제합니다.
데이터베이스 연결 개체는 DriverManager를 통해 가져옵니다. 가져올 때마다 데이터베이스에 적용하여 연결을 얻고 사용자 이름과 비밀번호를 확인해야 합니다.
사용자는 모든 요청에 대해 데이터베이스에서 링크를 얻어야 하며, 데이터베이스에 대한 연결을 생성하는 것은 일반적으로 상대적으로 많은 리소스를 소비하고 생성하는 데 오랜 시간이 걸립니다.
SQL 문이 실행될 때마다 연결이 끊겨 자원 낭비가 발생하고 데이터베이스 연결 자원이 잘 재사용되지 않는다.
웹 사이트가 하루에 100,000번의 방문을 받는다고 가정하면, 데이터베이스 서버는 100,000개의 연결을 생성해야 합니다. 이는 데이터베이스 리소스의 엄청난 낭비이며 데이터베이스 서버 메모리 오버플로 및 시스템 확장을 쉽게 유발할 수 있습니다.
솔루션: 데이터베이스 연결 풀
데이터베이스 연결 풀의 기본 아이디어:
데이터베이스에 대한 버퍼 풀을 구축하고, 미리 일정 개수의 연결 개체를 버퍼 풀에 넣어둡니다.
데이터베이스 연결을 얻으려면 버퍼 풀에서 개체를 가져오기만 하면 됩니다.
사용 후에는 다음 요청을 위해 버퍼 풀에 다시 넣어서 반복 생성 없이 리소스를 재사용할 수 있습니다.
데이터베이스 연결 풀에 유휴 연결 개체가 없으면 새 요청은 대기 대기열에 들어가 다른 스레드가 연결을 해제할 때까지 기다립니다.
JDBC의 데이터베이스 연결 풀은 javax.sql.DataSource 인터페이스를 사용하여 완성됩니다. DataSource는 java에서 공식적으로 제공하는 인터페이스입니다.
이를 사용할 때 개발자는 인터페이스를 직접 구현할 필요가 없으며 타사 도구를 사용할 수 있습니다.
C3P0은 일반적으로 사용되는 타사 구현입니다. 실제 개발에서는 C3P0을 직접 사용하여 데이터베이스 연결 풀 작업을 완료할 수 있습니다.
사용 단계:
① pom.xml에서 종속성을 가져옵니다.
- <dependency>
- <groupId>com.mchange</groupId>
- <artifactId>c3p0</artifactId>
- <version>0.9.5.2</version>
- </dependency>
② 코드 작성
- public class DataSourceTest {
- public static void main(String[] args) {
- try {
- //创建C3P0数据库连接池
- ComboPooledDataSource dataSource=new ComboPooledDataSource();
- dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
- dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8");
- dataSource.setUser("root");
- dataSource.setPassword("123456");
- //设置初始化连接个数
- dataSource.setInitialPoolSize(5);
- //设置最大连接个数(连接池中不够,可以继续申请,申请后最终的上限)
- dataSource.setMaxPoolSize(20);
- //当连接对象不够时,再次申请的连接对象个数
- dataSource.setAcquireIncrement(5);
- //设置最小连接数(当连接池中剩余2个连接对象时,就去申请 --> 提前做准备)
- dataSource.setMinPoolSize(2);
- Connection conn=dataSource.getConnection();
-
- //SQL操作...
-
- //将连接还回到数据库连接池中
- conn.close();
- } catch (PropertyVetoException e) {
- e.printStackTrace();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
알아채다:
기존 방법을 통해 얻은 연결: com.mysql.cj.jdbc.ConnectionImpl@3c153a1
C3P0에서 얻은 연결: com.mchange.v2.c3p0.impl.NewProxyConnection@6156496
그래서,모두 close 메소드를 호출하지만 구현 클래스가 다르기 때문에 메소드 재작성도 다릅니다. 이것이 인터페이스 다형성입니다.。
C3P0의 close 메소드는 연결 자원을 직접 파기하지 않고 연결을 데이터베이스 연결 풀로 반환한다.
위의 데이터베이스 연결 풀 매개변수 설정 방법은 Java 프로그램에서 직접 작성되었습니다.
이는 채택됨하드코드방법,구성을 변경할 때마다다시 컴파일해야 함, 새 클래스 파일을 생성하면 효율성이 너무 낮습니다.。
실제 개발에서는 C3P0의 구성 정보가 xml 파일에 정의되어 있으며 Java 프로그램은 구성 파일만 로드하면 데이터베이스 연결 풀의 초기화 작업이 완료됩니다.
이후에는 구성을 수정하고 다시 컴파일하지 않고 xml의 구성만 수정하면 됩니다.
사용 단계:
① 리소스 디렉터리에 c3p0-config.xml이라는 새 파일을 생성합니다.
② c3p0-config.xml에 구성 정보를 입력합니다.
- <?xml version="1.0" encoding="UTF-8"?>
- <c3p0-config>
- <!--配置连接池mysql-->
- <named-config name="C3P0Test">
- <!-- 指定连接数据源的基本属性 -->
- <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
- <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8</property>
- <property name="user">root</property>
- <property name="password">123456</property>
-
- <!-- 设置初始化连接个数 -->
- <property name="initialPoolSize">5</property>
- <!-- 设置最大连接个数(连接池中不够,可以继续申请,申请后最终的上限) -->
- <property name="maxPoolSize">20</property>
- <!-- 当连接对象不够时,再次申请的连接对象个数 -->
- <property name="acquireIncrement">5</property>
- <!-- 设置最小连接数(当连接池中剩余2个连接对象时,就去申请 -> 提前做准备) -->
- <property name="minPoolSize">2</property>
- </named-config>
- </c3p0-config>
③Java 프로그램 작성
- public class DataSourceTest {
- public static void main(String[] args) {
- try {
- //创建C3P0数据库连接池
- ComboPooledDataSource dataSource=new ComboPooledDataSource("C3P0Test");
- Connection conn=dataSource.getConnection();
- System.out.println(conn);
- //将连接还回到数据库连接池中
- conn.close();
- }catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
알아채다:
① ComboPooledDataSource 생성 방식의 파라미터는 c3p0-config.xml에 구성되어 있는 names-config 태그의 name 속성 값이다.
② 이때, JDBC 도구 클래스는 다음과 같이 수정할 수 있습니다.
- public class JdbcUtil {
- private static DataSource dataSource;
-
- static {
- dataSource = new ComboPooledDataSource("C3P0Test");
- }
-
- // 获取数据库连接对象
- public static Connection getConnection() throws SQLException {
- Connection conn = null;
- try {
- conn = dataSource.getConnection();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return conn;
- }
-
- // 释放资源(利用多态:Statement 和 PreparedStatement 都可以传进来)
- public static void release(Connection conn, Statement st, ResultSet rs) {
- try {
- if (rs != null) {
- rs.close();
- }
- if (st != null) {
- st.close();
- }
- if (conn != null) {
- conn.close();
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- public static Student findById(Integer idx) {
- Connection conn = null;
- PreparedStatement st = null;
- ResultSet rs = null;
- Student stu = null;
- try {
- conn = JdbcUtil.getConnection();
-
- String sql = "select * from student where id = ?";
- PreparedStatement ps = conn.prepareStatement(sql);
- //给占位符 ? 填充数据
- ps.setInt(1, idx);
- rs = ps.executeQuery();
- //取出结果集的数据
- while (rs.next()) {
- Integer id = rs.getInt(1);
- String name = rs.getString(2);
- Double score = rs.getDouble(3);
- Date birthday = rs.getDate(4);
- stu = new Student(id, name, score, birthday);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- try {
- //关闭链接,释放资源
- rs.close();
- st.close();
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- return stu;
- }
위 코드에서는 자리 표시자를 데이터로 채우고 결과 집합에서 데이터를 추출하는 작업이 너무 번거롭습니다.
Student 테이블에 100개의 속성이 있는 경우 데이터를 검색하려면 while 루프에 100개의 행을 작성해야 하며 자리 표시자를 채우는 데에도 많은 행이 필요할 수 있습니다.
해결책:DBUtils는 개발자가 데이터 캡슐화(결과 세트를 Java 객체에 매핑)를 완료하는 데 도움이 될 수 있습니다.
사용 단계:
① pom.xml에서 종속성을 가져옵니다.
- <dependency>
- <groupId>commons-dbutils</groupId>
- <artifactId>commons-dbutils</artifactId>
- <version>1.6</version>
- </dependency>
② 코드 작성
- public static Student findById(Integer idx) {
- Connection conn = null;
- Student stu = null;
- try {
- conn = JdbcUtil.getConnection();
-
- String sql = "select * from student where id = ?";
- //使用DBUtils
- QueryRunner qr = new QueryRunner();
- stu = qr.query(conn, sql, new BeanHandler<>(Student.class), idx);
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- try {
- //关闭链接,释放资源
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- return stu;
- }
이 시점에서 자리 표시자를 채우고 결과 집합을 가져오는 작업은 두 줄의 코드로 수행할 수 있습니다.
세부 사항:
① 쿼리 메소드는 4개의 매개변수를 전달해야 합니다.
연결 개체
SQL 문
ResultSetHandler 인터페이스의 구현 클래스 객체(변환된 객체의 유형은 Student.class여야 함)
자리표시자를 채우는 매개변수
② ResultSetHandler 인터페이스는 조회된 결과 세트를 Java 객체로 변환하는 데 사용됩니다. 다음과 같은 4가지 구현 클래스가 제공됩니다.
빈핸들러 | 결과 세트를 Java 객체(예: Student 객체)에 매핑합니다. |
빈리스트 핸들러 | 결과 세트를 목록 컬렉션(예: 목록)으로 매핑합니다.<Student > ) |
맵 핸들러 | 결과 세트를 Map 컬렉션 객체에 매핑 (예: 지도<String,Object> 키: 속성 이름; 값: 속성 값) |
맵리스트핸들러 | 결과 세트를 MapList 컬렉션으로 매핑합니다(예: List <Map<<String,Object> >) |
③ 자리 표시자를 채우는 매개변수는 가변 매개변수이므로 사용자의 다양한 요구를 충족시키기 위해 원하는 수의 매개변수를 전달할 수 있습니다.
④ 변환된 객체 클래스(Student 클래스)에는 매개변수가 없는 생성자가 있어야 합니다. 그렇지 않으면 프로그램에서 오류가 발생합니다.
이유:맨 아래 레이어는 Student.class를 통해 이 클래스를 찾은 다음반사 메커니즘이 클래스의 매개변수 없는 생성자를 찾아 해당 객체를 만듭니다.
⑤ 클래스의 속성 이름은 데이터베이스 테이블의 필드 이름과 정확히 동일해야 합니다.
왜냐하면 객체가 생성된 후 결과 집합을 기준으로 객체 속성에 값을 할당하면 이름을 기준으로 검색과 할당이 이루어지기 때문입니다.
MVC란 무엇입니까?
요청이 JavaWeb 애플리케이션에 들어간 후 Controller는 요청을 수신하고 비즈니스 로직 처리를 수행한 다음 최종적으로 결과를 사용자에게 반환합니다(View + Model).
초기에는 웹 애플리케이션을 운영할 때 사용자가 제어 계층에 직접 접근했고, 제어 계층은 데이터베이스를 직접 운영할 수 있었습니다.
서블릿--CRUD(추가, 삭제, 수정)-->데이터베이스
서블릿 코드에서: 요청, 응답, 뷰 점프 처리, JDBC 처리, 비즈니스 코드 처리, 논리 코드 처리
단점:프로그램이 너무 비대해지고 유지 관리에 도움이 되지 않습니다.
해결책: 다른 레이어를 추가해도 해결되지 않는 문제는 없습니다. 문제가 있으면 다른 레이어를 추가하세요!
모델
보다
컨트롤러(서블릿)
사용자 및 관리자 로그인을 예로 들어 보겠습니다.
컨트롤러 레이어:
- @WebServlet("/login")
- public class LoginServlet extends HttpServlet {
-
- private LoginService loginService = new LoginServiceImpl();
-
- /* 处理登录的业务逻辑*/
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- String username = req.getParameter("username");
- String password = req.getParameter("password");
- String type = req.getParameter("type");
- Object object = loginService.login(username,password,type);
- if(object != null){
- HttpSession session = req.getSession();
- switch (type){
- case "reader":
- Reader reader = (Reader) object;
- session.setAttribute("reader",reader);
- //跳转到用户的首页
- resp.sendRedirect("/book?page=1");
- break;
- case "admin":
- Admin admin = (Admin) object;
- session.setAttribute("admin",admin);
- //跳转到管理员的首页
- resp.sendRedirect("/admin?method=findAllBorrow&page=1");
- break;
- }
- }else{
- resp.sendRedirect("login.jsp");
- }
- }
-
- }
서비스 계층:
- public interface LoginService {
- //利用多态,动态返回不同类型的对象
- public Object login(String username,String password,String type);
- }
- public class LoginServiceImpl implements LoginService {
-
- private ReaderRepository readerRepository = new ReaderRepositoryImpl();
- private AdminRepository adminRepository = new AdminRepositoryImpl();
-
- @Override
- public Object login(String username, String password,String type) {
- Object object = null;
- //业务逻辑处理:根据type的值,来选择调用不同的登录方法,去查找不同的表
- switch (type){
- case "reader":
- object = readerRepository.login(username,password);
- break;
- case "admin":
- object = adminRepository.login(username, password);
- break;
- }
- return object;
- }
- }
Dao/Repository 레이어:
- public interface AdminRepository {
- public Admin login(String username,String password);
- }
- public interface ReaderRepository {
- public Reader login(String username,String password);
- }
- public class AdminRepositoryImpl implements AdminRepository {
- //管理员的登录方法(和数据库交互)
- @Override
- public Admin login(String username, String password) {
- Connection connection = JDBCTools.getConnection();
- String sql = "select * from bookadmin where username = ? and password = ?";
- PreparedStatement statement = null;
- ResultSet resultSet = null;
- Admin admin = null;
- try {
- statement = connection.prepareStatement(sql);
- statement.setString(1,username);
- statement.setString(2,password);
- resultSet = statement.executeQuery();
- if(resultSet.next()){
- admin = new Admin(resultSet.getInt(1),resultSet.getString(2),resultSet.getString(3));
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- JDBCTools.release(connection,statement,resultSet);
- }
- return admin;
- }
- }
- public class ReaderRepositoryImpl implements ReaderRepository {
- //用户的登录方法(和数据库交互)
- @Override
- public Reader login(String username, String password) {
- Connection connection = JDBCTools.getConnection();
- String sql = "select * from reader where username = ? and password = ?";
- PreparedStatement statement = null;
- ResultSet resultSet = null;
- Reader reader = null;
- try {
- statement = connection.prepareStatement(sql);
- statement.setString(1,username);
- statement.setString(2,password);
- resultSet = statement.executeQuery();
- if(resultSet.next()){
- reader = new Reader(resultSet.getInt(1),resultSet.getString(2),resultSet.getString(3),resultSet.getString(4),resultSet.getString(5),resultSet.getString(6),resultSet.getString(7));
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- JDBCTools.release(connection,statement,resultSet);
- }
- return reader;
- }
- }