JDBC(Java Datdoase Connectivity)是一个独立于特定数据库管理系统,通用的SQL数据库存取和操作的公共接口(一组API ),定义了用来访问数据库的标准Java类库,( java.sql,javax.sql )使用这些类库可以以—种标准的方法、方便地访问数据库资源。JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
导入mysql的驱动jar包
- 去mysql官网下载
- 在idea里面项目下建立一个lib文件夹,把解压后的jar文件放进去
- idea里面点击File——Project Structure——Modules——Dependencies——点击+号——JARs
加载驱动
mysql8以下版本:com.mysql.jdbc.Driver mysql8版本:com.mysql.cj.jdbc.Driver
//加载驱动
Driver driver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver(driver);
//或者这么写,反射,都一样
//Class.forName("com.mysql.cj.jdbc.Driver");
连接数据库
加载完驱动后既可以连接数据库了
DriverManager.getConnection()不知道哪个版本变强了,不需要注册驱动都能连接。高版本jdk下面代码一句话连接数据库,不需要注册驱动
mysql8以下版本:jdbc:mysql://localhost:3306/数据库名 mysql8版本:jdbc:mysql://localhost:3306/数据库名?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
Connection conn =null;
//url数据库连接workersm是数据库,root是用户,123456是密码
String url="jdbc:mysql://localhost:3306/workersm?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
conn = DriverManager.getConnection(url,"root","123456");
用配置文件的方式连接
新建一个文件jdbc.properties
放在包同级目录下,在里面写上配置信息,=别打空格
user=root
password=123456
url=jdbc:mysql://localhost:3306/workersm?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
driverClass=com.mysql.cj.jdbc.Driver
在数据库连接类中写到
public static void connDB() throws IOException, ClassNotFoundException, SQLException {
InputStream ips =DB.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties p = new Properties();
p.load(ips);
String user = p.getProperty("user");
String password = p.getProperty("password");
String url = p.getProperty("url");
String driverClass = p.getProperty("driverClass");
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);
}
这样写的好处是,如果要换数据库,只需要改配置文件里面的东西,不用修改java代码
执行SQL语句
增删改
//使用PreparedStatement执行数据表的正删改查操作
//增删改
public static void Execute1() throws SQLException, IOException, ClassNotFoundException {
//1.执行上面的方法,连接数据库
connDB();
//2.sql语句,???是通配符
String sql = "insert into worktypes values(?,?,?,?)";
//3.预编译sql语句,conn是数据库连接对象
PreparedStatement p = conn.prepareStatement(sql);
//4.填充占位符,是什么类型的数据就set什么类型,第一个参数为占位符的索引,从1开始,第二个参数为值
p.setInt(1,Types.NULL);
p.setString(2,"牛逼");
p.setDouble(3,333.3);
p.setDouble(4,44.4);
//5.执行sql
//boolean b1 = p.execute();//执行任何sql语句,如果返回的是数据集,就是true,否则false
int b2 = p.executeUpdate();//执行增删改,返回执行行数,或者没有
if(b2>0)
System.out.println("执行增删改成功"+b2);
else
System.out.println("失败");
//6.资源关闭
p.close();
conn.close();
}
批量操作
这种添加效率低得很,以下数据用了73秒
long start = System.currentTimeMillis();
for(int i=0;i<1000;i++){
p.executeUpdate();//执行增删改,返回执行行数,或者没有
}
long end =System.currentTimeMillis();
System.out.println("一共用时:"+(end-start)/1000+"秒");
Batch累计sql的方式
需要在连接数据库的url添加上参数
rewriteBatchedStatements=true
url=jdbc:mysql://localhost:3306/workersm?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&rewriteBatchedStatements=true
每500条sql执行一次,10w条用时31秒,改为每5000条执行一次,用时4秒,速度和这个有直接关系
long start = System.currentTimeMillis();
for(int i=0;i<100000;i++){
p.addBatch();//累计sql
if(i%500==0){//累计500条sql执行1次
p.executeBatch();//执行累计的sql
p.clearBatch();//清空
}
if(i==99999){
p.executeBatch();//执行累计的sql
p.clearBatch();//清空
}
}
long end =System.currentTimeMillis();
System.out.println("一共用时:"+(end-start)/1000+"秒");
Batch统一提交数据,只用了1秒左右
long start = System.currentTimeMillis();
conn.setAutoCommit(false);//设置不允许自动提交数据
for(int i=0;i<100000;i++){
p.addBatch();//累计sql
if(i%5000==0){//累计5000条sql执行1次
p.executeBatch();//执行累计的sql
p.clearBatch();//清空
}
if(i==99999){
p.executeBatch();//执行累计的sql
p.clearBatch();//清空
}
}
conn.commit();//统一提交数据
long end =System.currentTimeMillis();
System.out.println("一共用时:"+(end-start)/1000+"秒");
查
写一个对象类
public class Worktypes {
private int id;
private String workname;
private double wtdaymoney;
private double wthourmoney;
public void setId(int id) {
this.id = id;
}
public void setWorkname(String workname) {
this.workname = workname;
}
public void setWtdaymoney(double wtdaymoney) {
this.wtdaymoney = wtdaymoney;
}
public void setWthourmoney(double wthourmoney) {
this.wthourmoney = wthourmoney;
}
public int getId() {
return id;
}
public String getWorkname() {
return workname;
}
public double getWtdaymoney() {
return wtdaymoney;
}
public double getWthourmoney() {
return wthourmoney;
}
public Worktypes(int id, String workname, double wtdaymoney, double wthourmoney) {
this.id = id;
this.workname = workname;
this.wtdaymoney = wtdaymoney;
this.wthourmoney = wthourmoney;
}
}
查询代码
//查询
public static void Execute2() throws SQLException, IOException, ClassNotFoundException {
//1.执行上面的方法,连接数据库
connDB();
//2.sql语句
String sql = "select *from worktypes";
//3.预编译sql
PreparedStatement ps = conn.prepareStatement(sql);
//4.设置参数,没有不写
//5.执行查询返回结果集
ResultSet rs = ps.executeQuery();
//6.处理结果集
//这里只实例化了一个对象,用的是if
if(rs.next()){//判断集合的下一条是否有数据,如果有,就返回true
//获取当前这条数据的各个字段
//1.1通过索引获取,索引值从1开始
int id = rs.getInt(1);
String workname =rs.getString(2);
//1.2通过字段名获取
double wtdaymoney=rs.getDouble("wtdaymoney");
double wthourmoney=rs.getDouble("wthourmoney");
//实例化对象
Worktypes wt = new Worktypes(id,workname,wtdaymoney,wthourmoney);
System.out.println("这是"+wt);
}
//7.关闭资源
conn.close();
ps.close();
rs.close();
}
用ArrayList获取全部值,第6步
ArrayList<Worktypes> wts = new ArrayList();
while (rs.next()){
int id = rs.getInt(1);
String workname =rs.getString(2);
double wtdaymoney=rs.getDouble("wtdaymoney");
double wthourmoney=rs.getDouble("wthourmoney");
Worktypes wt = new Worktypes(id,workname,wtdaymoney,wthourmoney);
wts.add(wt);
}
//获取里面第3条数据的workname字段值
System.out.println(wts.get(2).getWorkname());
通过反射赋值对象
//5.执行查询返回结果集
ResultSet rs = ps.executeQuery();
//获取结果集的元数据,里面包含了这个数据集的一些信息
ResultSetMetaData rsmd = rs.getMetaData();
int c = rsmd.getColumnCount();//获取结果集的列数
if(rs.first()){
Worktypes wts = new Worktypes();
//处理结果集中每一行数据中的每一列
for(int i =0;i<c;i++){
//获取列值
Object cvalue = rs.getObject(i+1);
//获取列的列名。获取查询出来的数据集的标签名用 rsmd.getColumnLabel()
String cname = rsmd.getColumnName(i+1);
//通过反射给wts对象的cname列赋值cvalue
Field field = Worktypes.class.getDeclaredField(cname);
field.setAccessible(true);
field.set(wts,cvalue);
}
}
事务
数据一旦提交,就不能回滚了
DDL操作一旦执行就制动提交了 DML默认情况下执行,自动提交,可以通过set autocommit = false方式取消DML自动提交
代码
执行sql语句的方法
//执行一条增删改数据
public static void Execute1UpdataSQL(Connection conn,String sql,Object ...args) throws SQLException {
PreparedStatement p = conn.prepareStatement(sql);//预编译sql
//填充占位符
for(int i=0;i< args.length;i++){
p.setObject(i+1,args[i]);
}
p.executeUpdate();
p.close();//事务里面单条sql语句不能关闭连接,可以关闭p
}
执行多条sql语句形成事务
public static void main(String[] args) throws SQLException {
Connection connection = null;
try {
connection = MysqlDB.getConnection();//获取一个连接
connection.setAutoCommit(false);//设置不自动提交
//执行第一个sql语句,不提交
String sql1 = "delete from worktypes where id>5";
MysqlDB.Execute1UpdataSQL(connection,sql1);
//执行第二个sql语句,不提交
String sql2 = "insert into worktypes values(?,?,?,?)";
MysqlDB.Execute1UpdataSQL(connection,sql2, Types.NULL,"事务测试",888.88,99.99);
connection.commit();//最后统一提交前面的sql语句
} catch (Exception throwables) {
throwables.printStackTrace();
try {
connection.rollback();//如果发生错误,就回滚
} catch (SQLException e) {
e.printStackTrace();
}
}finally {
connection.close();
//如果不想关闭连接,连接对象还要接着用,记得把自动提交打开connection.setAutoCommit(true)
}
}
属性
原子性
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性
隔离性
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。