数据库的操作步骤:
1:导入数据库的驱动jar包----> 导入实现类
2:加载驱动 -----> 通知他我要连贯哪个数据库
3:取得连贯------>让我和数据库产生关系
4:取得预处理语句对象->让我 发送sql指令到数据库
5:执行SQL命令;--->预处理语句对象.execute("SQL 命令行");
6:开释资源 -------> 切断我和数据库的分割
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import org.junit.Test;
public class JDBCTest {
@Test
public void testJDBC() throws Exception {
// 1.加载驱动//DriverManager.registerDriver(new com.mysql.jdbc.Driver()); Class.forName("com.mysql.jdbc.Driver");//间接应用此形式:因为底层曾经实现下面的步骤// 2:取得连贯----->让java程序和数据库建立联系 . 让程序和数据库产生关系Connection connection = DriverManager.getConnection("jdbc:mysql://IP地址:端口号(默认3306)/数据库的名字", "root", "root");// 3:取得预处理语句对象-->让java发送SQL语句到数据库Statement statement = connection.createStatement();// 4.执行须要执行的SQL语句statement.execute("create table girl(id int , name varchar(20),age int);");// 4:开释资源statement.close();connection.close();
}
}
如何找到你要操作的数据集库:
写死 jdbc:不同的数据库的厂商名://ip地址:端口号/数据库的库名
操作mysql形式1:jdbc:mysql://localhost:3306/数据库的库名
操作mysql形式2:jdbc:mysql://127.0.0.1:3306/数据库的库名
如果 操作的数据库在本机 ,并且数据库的端口号是 3306
能够简写 形式:jdbc:mysql:///数据库的库名
抽取工具类:DAO标准的设计
标准DAO组件的类名以及包名. 域名java培训倒写.模块名.组件名----> cn.wolfcode.smis
1.定义domain包以及类名(操作DAO都是去操作一张表,表应该对应着一个javabean, 取一个非凡的名字domain)
cn.wolfcode.smis.domain
Student.java
2.定义DAO包以及DAO接口.
cn.wolfcode.smis.dao
IxxxDAO---> XXX示意domain.
IStudentDAO.java
3.定义DAO实现类的包以及实现类
cn.wolfcode.smis.dao.impl
StudentDAOImpl.java
4.测试类
cn.wolfcode.smis.test
StudentDAOImplTest.java
5.工具类
cn.wolfcode.smis.util
JDBCUtil.java
标准的开发步骤---实现保留操作:
1.创立数据库表.
2.依据数据库表创立对应的domain包和类.
留神:表的列的名字和类型与类的属性名和类型要一一匹配.
3.通过domain来创立DAO包以及接口.
4.依据接口生成实现类.
5.依据接口/实现类生成测试类.实现测试的代码.
6.实现一个办法,测试一个办法,测试通过,再写写一个办法.
预编译语句对象-PreparedStatement:
预编译语句对象-PreparedStatement:
PreparedStatement是动态语句对象的子类.所以,父类中的所有的办法都能够调用到.然而个别咱们不会调用.调用本人特有的办法.
预编译语句对象是通过带有占位符的SQL模板来创建对象的.
SQL模板:带有占位符?的SQL.雷同的操作,SQL语句其实是一样的,仅仅是参数不一样而已.
参数确定不了,应用占位符?来示意.
常见API:
- 获取预编译语句对象.
应用数据库连贯对象:
PreparedStatement prepareStatement(String sql); 传递的是SQL模板,有占位符
2.在执行SQL之前,肯定要给占位符设置值.
void setObject(int parameterIndex,Object x):
parameterIndex: 参数索引,在JDBC中,索引是从1开始的.
3.执行SQL命令:
留神,不要调用父类带有参数的办法.
DQL操作:增删改
在JDK中,把如何解析后果集的操作,封装到了一个对象中.ResultSet.
需要:
1.查问id为10的学生信息
2.查问所有的学生信息.
ResultSet对象:
把如何解析后果集的过程封装成了对象.
boolean next(): 往下挪动一行,如果返回true,示意有数据,否则没有数据.要获取数据,肯定要调用next办法.
Object getObject(int columnIndex): 依据列的地位来取数据.索引从1开始.
Object getObject(String columnLabel): 依据列名来获取数据
优化:
1.连贯数据库四因素反复写很多遍(抽取到公共的地位,只写一遍).
2.Class.forName加载字节码,字节码只会加载一次(应用动态代码块去加载注册驱动).
3.硬编码.(连贯数据库四因素).
4.代码反复(DML操作, DQL操作).
5.每次获取数据库连贯对象,用完间接就敞开了.节约.
代码:
配置文件:
符号javaBean的Student类:
DAO接口的定义:
接口的测试类:
JDBC的工具类:
package cn.wolfcode.smis.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
//工具类
public class MyJDBCUtil {
// 私有化结构器
private MyJDBCUtil() {
}
static Properties p = new Properties();
// 动态代码块
static {
try { // 加载配置文件 p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("pd.properties")); // 加载驱动器 Class.forName(p.getProperty("driverClassName"));} catch (Exception e) { e.printStackTrace();}
}
// 取得连贯对象
public static Connection getConnection() {
Connection connection = null;try { connection = DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password"));} catch (Exception e) { e.printStackTrace();}return connection;
}
// 敞开资源
public static void close(Connection connection, Statement statement) {
try { if (statement != null) { statement.close(); }} catch (SQLException e) { e.printStackTrace();}try { if (connection != null) { connection.close(); }} catch (SQLException e) { e.printStackTrace();}
}
// 重载敞开资源
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
try { if (statement != null) { statement.close(); }} catch (SQLException e) { e.printStackTrace();}try { if (connection != null) { connection.close(); }} catch (SQLException e) { e.printStackTrace();}try { if (resultSet != null) { resultSet.close(); }} catch (SQLException e) { e.printStackTrace();}
}
}
接口的实现类:
package cn.wolfcode.smis.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import cn.wolfcode.smis.dao.IStudentDAO;
import cn.wolfcode.smis.domain.Student;
import cn.wolfcode.smis.util.MyJDBCUtil;
public class StudentDAOImpl implements IStudentDAO {
@Override
public void insert(Student student) {
// 取得连贯对象Connection connection = MyJDBCUtil.getConnection();// 取得预编译对象PreparedStatement statement = null;try { statement = connection.prepareStatement("insert into student(id,name,age) values(?,?,?)"); // 设置站位符 statement.setObject(1, student.getId()); statement.setObject(2, student.getName()); statement.setObject(3, student.getAge()); // 执行 statement.execute();} catch (Exception e) { e.printStackTrace();} finally { // 敞开资源 MyJDBCUtil.close(connection, statement);}
}
@Override
public void delete(Long id) {
// 取得连贯对象Connection connection = MyJDBCUtil.getConnection();// 取得预编译对象PreparedStatement statement = null;try { statement = connection.prepareStatement("delete from student where id = ?"); // 设置站位符 statement.setObject(1, id); // 执行 statement.execute();} catch (Exception e) { e.printStackTrace();} finally { // 敞开资源 MyJDBCUtil.close(connection, statement);}
}
@Override
public void update(Student student) {
// 取得连贯对象Connection connection = MyJDBCUtil.getConnection();// 取得预编译对象PreparedStatement statement = null;try { statement = connection.prepareStatement("update student set name = ? ,age = ? where id = ?"); // 设置站位符 statement.setObject(1, student.getName()); statement.setObject(2, student.getAge()); statement.setObject(3, student.getId()); // 执行更新办法 statement.executeUpdate();} catch (Exception e) { e.printStackTrace();} finally { // 敞开资源 MyJDBCUtil.close(connection, statement);}
}
@Override
public Student selectOne(Long id) {
// 取得连贯对象Connection connection = MyJDBCUtil.getConnection();// 取得预编译对象PreparedStatement statement = null;Student student = null;try { statement = connection.prepareStatement("select * from student where id = ?"); // 设置站位符 statement.setObject(1, id); // 执行查问,返回后果集 ResultSet resultSet = statement.executeQuery(); if (resultSet.next()) {// 后果集中有元素 // 失去后果集中的元素---->后果集中是一个个字段对应的值,听过字段名取得值,而后创立个对象返回 Object name = resultSet.getObject("name"); Object age = resultSet.getObject("age"); // 创建对象 student = new Student(id, (String) name, (Integer) age); }} catch (Exception e) { e.printStackTrace();} finally { // 敞开资源 MyJDBCUtil.close(connection, statement);}return student;
}
@Override
public List<Student> selectAll() {
// 创立ListList<Student> list = new ArrayList<>();// 取得连贯对象Connection connection = MyJDBCUtil.getConnection();// 取得预编译对象PreparedStatement statement = null;try { statement = connection.prepareStatement("select * from student"); // 执行查问,返回后果集 ResultSet resultSet = statement.executeQuery(); // if (resultSet.next()) {// 后果集中有元素 // 用while循环将后果集中所有的元素取出来 while (resultSet.next()) { // 失去后果集中的元素---->后果集中是一个个字段对应的值,听过字段名取得值,而后创立个对象返回 Object id = resultSet.getObject("id"); Object name = resultSet.getObject("name"); Object age = resultSet.getObject("age"); // 创建对象并将对象增加进list中 list.add(new Student((Long) id, (String) name, (Integer) age)); }} catch (Exception e) { e.printStackTrace();} finally { // 敞开资源 MyJDBCUtil.close(connection, statement);}return list;
}
}
上述代码仍然有反复:应用模板办法可再进行优化。
注入问题:经典案例登录平安问题;
@Test
public void testLogin() throws Exception {
// 模仿账户的登录,了解 statement和preparestatement的区别String username = "will";// String password = "123456";// 注入问题String password = "' or '1'='1";// loginByStatement(username, password);// loginByPrepareStatement(username, password);loginByPrepareStatement1(username, password);
}
// 没有应用预处理语句对象:不平安 String password = "' or '1'='1";时能够屏蔽SQL语句的语义。导致胜利登录
private void loginByStatement(String username, String password) throws Exception {
// 验证 username 和 password 在数据库中的表 是否存在这一条数据Connection connection = JDBCUtil.getConnection();Statement statement = connection.createStatement();String sql = "select * from user where username = '" + username + "' and password = '" + password + "'";ResultSet resultSet = statement.executeQuery(sql);if (resultSet.next()) { System.out.println("登录胜利");} else { System.out.println("账户或者明码谬误,登录失败");}JDBCUtil.close(connection, statement, resultSet);
}
// 模仿登录案例--->应用预处理对象:平安
private void loginByPrepareStatement1(String username, String password) throws Exception {
// 把参数和数据库中的客户信息比照,雷同能力登录胜利// 1连贯数据库Connection connection = JDBCUtil.getConnection();// 2取得预处理语句对象PreparedStatement statement = connection .prepareStatement("select * from user where username = ? and password = ?");// 2.1设置占位符statement.setObject(1, username);statement.setObject(2, password);// 3查问后果ResultSet resultSet = statement.executeQuery();// 判断数据库中是否有这条用户数据if (resultSet.next()) { System.out.println("登录胜利!");} else { System.out.println("账户不存在!登录失败!!");}// 敞开资源JDBCUtil.close(connection, statement, resultSet);
}
事务:经典案例,转账平安问题。
冀望后果:如果转账胜利,那么数据发生变化.如果转账失败,那么账户余额不应该扭转,应该还是转账之前的状态.
下面这种操作,就是数据库中的事务操作.
事务(Transaction,简写为tx):
在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。
为确保数据库中数据的一致性,数据的操纵该当是离散的成组的逻辑单元:
当每个逻辑操作单元全副实现时,数据的一致性能够放弃,
而当这个单元中的一部分操作失败,整个事务应全副视为谬误,所有从起始点当前的操作应全副回退到开始状态。
事务的操作:
先定义开始一个事务,而后对数据作批改操作,这时如果提交(commit),这些批改就永恒地保留下来,如果回退(rollback),数据库管理系统将放弃您所作的所有批改而回到开始事务时的状态。
事务:指形成单个逻辑工作单元的操作汇合
事务处理:保障所有事务都作为一个工作单元来执行,即便呈现了故障,都不能扭转这种执行形式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最后状态
说白了:
1.须要把多个操作放在一起作为一个整体.
2.全副胜利,才叫胜利,否则一个失败,全副都失败.
事务的ACID属性:
- 原子性(Atomicity):原子在化学中,是最小单位,不能够再宰割了.
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都产生,要么都不产生。
- 一致性(Consistency):保证数据的完整性.
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被毁坏)
- 隔离性(Isolation):
事务的隔离性是指一个事务的执行不能被其余事务烦扰,即一个事务外部的操作及应用的数据对并发的其余事务是隔离的,并发执行的各个事务之间不能相互烦扰。
- 持久性(Durability):
持久性是指一个事务一旦被提交,它对数据库中数据的扭转就是永久性的,接下来的其余操作和数据库故障不应该对其有任何影响
事务相干的细节:
1):默认状况下,事务在执行完DML操作就主动提交.
2):查问操作,其实是不须要事务的.然而,个别的,咱们在开发中都把查问放入事务中.
3):开发中,代码完全正确,没有异样,然而就是数据库中数据不变.
意识:可能没有提交事务.
4):在MySQL中,只有InnoDB存储引擎反对事务,反对外键,MyISAM不反对事务.
5):当前事务咱们不应该在DAO层解决,应该在service层管制(提出).
6):事务在解说框架和我的项目的时候都会再讲.
事务操作,在一次连贯中进行屡次操作.须要将多个操作放在一个整体中,就须要应用事务.
操作模板:
try{
// 手动开启一个事务
conn.setAutoCommit(false);
操作1...
操作2...
操作N...
// 全副胜利,提交事务
conn.commit();
} catch(Exception e){
// 异样代码解决
// 如果失败,须要回滚
conn.rollback();
} finally{
// 敞开资源
}
留神:
只有在调用了commit办法之后,数据库才会真正的发生变化.全副胜利,调用commit办法.如果失败了肯定要调用rollback办法.
起因:在开启事务的时候,就有一个事务锁的存在.必须要调用commit或者rollback才能够开释.
@Test
public void testTransaction() {
// 模仿转账的成果 西门吹雪 转账给 菠萝吹雪 1000钱。如果转账过程中出现异常,则金额不应该产生扭转。// 1:先判断 西门吹雪 有没有钱Connection connection = JDBCUtil.getConnection();PreparedStatement statement = null;ResultSet resultSet = null;try { // 勾销主动提交,让残缺的事物执行完后再进行收动提交 connection.setAutoCommit(false); statement = connection.prepareStatement("select * from account where name = '西门吹雪' and balance >= 1000"); resultSet = statement.executeQuery(); // 只有金额足够能力转账 if (resultSet.next()) { System.out.println("开始转账"); // 西门吹雪的 钱 缩小1000 statement.executeUpdate("update account set balance = balance - 1000 where name = '西门吹雪'"); // int ret = 10 / 0;//制作异样---->如果转账过程中出现异常,则金额不应该产生扭转。// 菠萝吹雪金莲的钱 减少 1000 statement.executeUpdate("update account set balance = balance + 1000 where name = '菠萝吹雪'"); // 手动提交事务 connection.commit(); } else { System.out.println("没钱!!!,我的二叔是公安局长"); }} catch (Exception e) { System.out.println("转账异常中断!"); e.printStackTrace(); // 回退:出现异常时,将事务回退到最开始的状态。---->只有呈现异常中断时才须要回退,所以放到catch语句中 try { connection.rollback(); } catch (SQLException e1) { e1.printStackTrace(); }} finally { // 敞开资源:无论如何最初都要敞开资源 JDBCUtil.close(connection, statement, resultSet);}
}
取得主动生成的主键:取得注册后信息
1.设置一个标记,给数据库说,我要主动生成的主键.
在获取预编译语句对象的时候,传递要获取的标记.
Statement.RETURN_GENERATED_KEYS .
2.获取主动生成的主键.
// 模仿注册后显示id和注册时的信息
@Test
public void testInsert() throws Exception {
// 注册时的输出的信息String username = "天河首富";String password = "lh0030";// 取得连贯对象Connection connection = JDBCUtil.getConnection();String sql = "insert into user (username,password) values(?,?)";// 取得预处理语句对象PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);// 取得主动生成的主键// 设置占位符的值statement.setObject(1, username);statement.setObject(2, password);// 执行更新操作statement.executeUpdate();// 把主动生成的主键放到了一个后果集对象中ResultSet keys = statement.getGeneratedKeys();// 如果插入胜利就返回主键和插入的信息if (keys.next()) { // 获取主动生成的主键 long id = (long) keys.getObject(1); System.out.println(id + " " + username + " " + password);}// 敞开资源JDBCUtil.close(connection, statement, keys);
}
数据库连接池:
目前咱们操作数据库的形式:
1.获取数据库连贯对象.
2.执行SQL命令
3.解析后果集.
4.敞开资源(后果集对象,预编译语句对象,数据库连贯对象)
执行完了一个SQL命令之后,就会敞开数据库连贯对象.问题就出在这里.
为什么必须应用数据库连接池:
一般的JDBC数据库连贯(Connection对象)应用 DriverManager 来获取,每次向数据库建设连贯的时候都要将 Connection 加载到内存中,再验证用户名和明码(得破费0.05s~1s的工夫),数据库的连贯是比拟低廉的(创立的老本比拟大)。
须要数据库连贯的时候,就向数据库要求一个,执行实现后再断开连接。这样的形式将会耗费大量的资源和工夫。
数据库的连贯资源并没有失去很好的反复利用.若同时有几百人甚至几千人在线,频繁的进行数据库连贯操作将占用很多的系统资源,重大的甚至会造成服务器的解体。
对于每一次数据库连贯,应用完后都得断开。否则,如果程序出现异常而未能敞开,将会导致数据库系统中的内存透露,最终将导致重启数据库。
这种开发不能管制被创立的连贯对象数,系统资源会被毫无顾及的调配进来,如连贯过多,也可能导致内存透露,服务器解体.
说白了:
以后的操作数据库的形式是,破费了大量的资源和工夫创立的十分低廉的数据库连贯对象,应用一次之后就敞开了.次要存在的问题是,没有充沛应用到数据库连贯对象.应用连接池来解决这个问题.
池化技术:治理资源对象,达到最高的效率.
池化技术可能缩小资源对象的创立次数,进步程序的性能,特地是在高并发下这种进步更加显著。应用池化技术缓存的资源对象有如下独特特点:
1,对象创立工夫长;
2,对象创立须要大量资源;3,对象创立后可被重复使用。比方thread,connection等对象都具备下面的几个独特特点。
在Java中,连接池应用javax.sql.DataSource接口来示意连接池.
DataSource(数据源)和连接池(Connection Pool)是同一个.
留神:DataSource仅仅只是一个接口,由各大服务器厂商来实现(Tomcat,JBoss).
罕用的DataSource的实现:
DBCP: Spring框架举荐的
druid: 阿里巴巴的连接池(号称Java语言中性能最好的连接池)德鲁伊.
==============================================================
应用连接池和不应用连接池的区别在哪里?
如何获取Connection对象:
未应用连接池:
Connection conn = DriverManager.getConnection(url,username,password);
应用了连接池:
datasource对象.getConnection();// 返回的连贯对象曾经是一个性能加强的连贯对象了.
只有获取了Connection对象,接下来的操作和以前是截然不同的.
关键在于:如何创立DataSource对象.
如何开释Connection对象(Connection对象.close()):
未应用连接池: 是和数据库服务器断开.
应用了连接池: 还给连接池.
应用数据库连接池创立JDBC的工具类:
形式1:
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
//JDBC工具类
public class JDBCUtil {
// 私有化结构器
private JDBCUtil() {
}
static Properties p = new Properties();
static {
try { p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties")); Class.forName(p.getProperty("driverClassName"));} catch (IOException e) { System.err.println("你的配置文件加载不到"); e.printStackTrace();} catch (ClassNotFoundException e) { e.printStackTrace();}
}
// 取得数据库的连贯对象
public static Connection getConnection() {
// 取得 资源文件夹中的db.properties文件中数据库连贯信息Connection connection = null;try { connection = DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password"));} catch (SQLException e) { e.printStackTrace();}return connection;
}
}
形式2:
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;//德鲁伊的jar包
//JDBC工具类
public class JDBCUtil {
// 私有化结构器
private JDBCUtil() {
}
static DataSource source = null;
static {
try { Properties p = new Properties(); p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties")); //这种形式创建对象 source = DruidDataSourceFactory.createDataSource(p);} catch (IOException e) { e.printStackTrace();} catch (Exception e) { e.printStackTrace();}
}
// 取得数据库的连贯对象
public static Connection getConnection() {
Connection connection = null;try { connection = source.getConnection();} catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace();}return connection;
}
//省略关流操作,关流没变
}