知识点:
AUTO_INCREMENT----自动增长
PRIMARY KEY-----主键
DEFAULT CHARSET=uft8----缺省字符集
初始化中jdbc:mysql的意思是连接mysql的协议
端口是通讯连接比如mysql是3306
一:建立与数据库的连接
步骤 | 1 | 2 | 3 | 4 | 5 |
做法 | 导入jar包 | 初始化驱动 | 与数据库建立联系 | 创建statement去执行 | 关闭数据库 |
注意Statement时,除了字符串要用单引号,其他都是双引号,整数和字符串两边都得加"+"号
package jbbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class tt {
public static void main(String[] args) {
try{
//com.mysql.jdbc.Driver就在jar包里面
Class.forName("com.mysql.jdbc.Driver");
System.out.println("驱动初始化完成!");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
//建立与数据库的Connection连接
//jdbc.mysql是协议
//characterEncoding作用是把接受的数据进行重新编码
//使用try-with-resource的方式自动关闭连接
try(
Connection a=DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
"root","admin");
//创建Statement并且执行
Statement b=a.createStatement();
){
for(int n=1;n<20;n++){
String ss="insert into hero values(null,"+"'虾兵蟹
将"+n+"',"+460.0f+","+90+")";
b.execute(ss);//执行插入数据
}
String ss="insert into hero values(null,"+"'树神'"+","+460.0f+","+90+")";
b.execute(ss);//执行插入数据
}catch(SQLException e){
e.printStackTrace();
}
}
}
二、增删改查
CRUD是最常见的数据库操作
C | R | U | D |
增加Create | 读取查询Retrieve | 更新Update | 删除Delete |
操作与增加类似:
//删除
String aa="delete from hero whero id=5";
//更新
String bb="update hero set name='僵尸' whero id=10";
执行即可
查询语句有所不同
利用类import java.sql.ResultSet;
package jbbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
public class tt {
public static void main(String[] args) {
try{
//com.mysql.jdbc.Driver就在jar包里面
Class.forName("com.mysql.jdbc.Driver");
System.out.println("驱动初始化完成!");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
//建立与数据库的Connection连接
//jdbc.mysql是协议
//characterEncoding作用是把接受的数据进行重新编码
try(
Connection a=DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
"root","admin");
//创建Statement并且执行
Statement b=a.createStatement();
){
String sql="select * from hero";
//执行查询语句,并把结果返回给ResultSet
ResultSet rr=b.executeQuery(sql);
while(rr.next()){
int id=rr.getInt("id");//可以使用字段名
String name=rr.getString(2);//也可以使用字段的顺序
Float hp=rr.getFloat("hp");
int damage=rr.getInt(4);
System.out.printf("%d\t%s\t%f\t%d%n",id,name,hp,damage);
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
获取数据总数
String cc="select count(*) from hero";
ResultSet ccc=b.executeQuery(cc);
int number=0;
while(ccc.next()){
number=ccc.getInt(1);
}
System.out.println("共有"+number+"数据");
三、编译Statement(preparedStatement)
和Statement一样也是用来执行sql语句的,不同的是需要根据sql语句创建PreparedStatement,除此之外还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接
注: 这是JAVA里唯二的基1的地方,另一个是查询语句中的ResultSet也是基1的
package jbbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class tt {
public static void main(String[] args) {
try{
//com.mysql.jdbc.Driver就在jar包里面
Class.forName("com.mysql.jdbc.Driver");
System.out.println("驱动初始化完成!");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
String sql="insert into hero value(null,?,?,?)";
try(
Connection a=DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
"root","admin");
//根据sql创建PreparedStatement
PreparedStatement b=a.prepareStatement(sql);
){
b.setString(1, "美杜莎孙子");
b.setFloat(2, 568.0f);
b.setInt(3, 450);
b.execute();
}catch(SQLException e){
e.printStackTrace();
}
}
}
PreparedStatement的优点
参数设置 | PreparedStatement可读性好也不易犯错;而Statement还需要字符串拼接,就比较麻烦 |
性能表现 | PreparedStatement有预编译机制性能比Statement更快 |
防止SQL注入式攻击 | PreparedStatement是参数设置就可以避免这种问题,如果数据是用户提交的 |
四、execute与executeUpdate的区别
相同点 | 都可以执行增加、删除、修改,方法一样,参考第二学的查询 |
不同点 | 1、execute可以查询,executeUpdate不能执行查询语句 2、execute返回boolean类型,true表示执行的是查询语句,false表示执行的是 增删改 executeUpdate返回的是int,表示有多少条数据受到了影响 |
五、特殊操作
1、获取自增长id
在Statement通过execute或者executeUpdate执行完插入语句后,MySql会为新插入的数据分配一个自增长id。当然前提是这个表的id设置了自增长,AUTO_INCREMENT就表示自增长,代码如下
CREATE TABLE hero (
id int(11) AUTO_INCREMENT,
...
}
但是无论是execute还是executeUpte都不会返回这个自增长id是多少,需要通过Statement和getGeneratedKeys获取该id
Statement.RETURN_GENERATED_KEYS参数,以确保会返回自增长ID。 通常情况下不需要加这个,有的时候需要加,所以先加上,保险一些,见如下代码
package jbbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class tt {
public static void main(String[] args) {
try{
Class.forName("com.mysql.jdbc.Driver");
System.out.println("驱动初始化完成!");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
String sql="insert into hero value(null,?,?,?)";
try(
Connection a=DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
"root","admin");
//根据sql创建PreparedStatement
PreparedStatement b=a.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
){
b.setString(1, "恐龙");
b.setFloat(2, 568.0f);
b.setInt(3, 450);
b.execute();
// JDBC通过getGeneratedKeys获取该id
ResultSet rs=b.getGeneratedKeys();
if(rs.next()){
int id=rs.getInt(1);
System.out.println("该id为:"+id);
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
获取表的元数据
元数据概念:和数据库服务器相关的数据,比如数据库版本,有哪些表,表有哪些字段,字段类型是什么等等,代码如下
package jbbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
public class dbm {
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
Class.forName("com.mysql.jdbc.Driver");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
try(Connection c=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root","admin");){
//查看数据库层面的元数据
DatabaseMetaData dbmd=c.getMetaData();
//获取数据库服务器产品名称
System.out.println(dbmd.getDatabaseProductName());
//获取数据库服务器产品版本号
System.out.println(dbmd.getDatabaseProductVersion());
//获取数据库服务器用作类别和表名之间的分割符
System.out.println(dbmd.getCatalogSeparator());
//获取驱动版本
System.out.println(dbmd.getDriverVersion());
System.out.println("可用的数据库名称如下:");
ResultSet rs=dbmd.getCatalogs();
while(rs.next()){
System.out.println("数据库名称:\t"+rs.getString(1));
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
六、事务
作用:在事务的多个操作,要么都成功要么都失败,见如下代码
使用方法:通过 Connection对象.setAutoCommit(false);关闭自动提交
Connection对象.commit();进行手动提交
try(Connection c......
Statement s=c.createStatement();){
c.setAutoCommit(false);//关闭自动提交
String sql1=..........;
s.execute(sql1);
String sql2=..........;
s.execute(sql2);
c.commit();//手动提交
}catch.....
注意在Mysql中,只有当表的类型是INNODB的时候才支持事务,所以需要把表的类型设置为INNODB,否则无法观察到事务,代码如下
//修改表的类型为INNODB的SQL
alter table hero ENING=innodb;
//查看表的类型
show table status from how2java;
不过有个前提,就是当前的MYSQL服务器本身要支持INNODB,如果不支持,看 开启MYSQL INNODB的办法
七、ORM ------对象和关系数据库的映射
ORM=Object Relationship Database Mapping
简单的说就是一个对象对应数据库里的一条记录
注意的是:框架一环接一环
package jbbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import charactor.Hero;
public class YS {
public static Hero get(int id) {
Hero hero =null;
try{
Class.forName("com.mysql.jdbc.Driver");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
try(Connection c=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root","admin");
Statement s=c.createStatement();){
String sql="select * from hero where id="+id;
ResultSet rs=s.executeQuery(sql);
//因为id是唯一的,ResultSet最多只能有一条记录,所以使用if代替while
if(rs.next()){
hero=new Hero();
String name=rs.getString(2);
float hp=rs.getFloat("hp");
int damage=rs.getInt(4);
hero.name=name;
hero.hp=hp;
hero.damage=damage;
hero.id=id;
}
}catch(SQLException e){
e.printStackTrace();
}
return hero;
}
public static void main(String[]args){
Hero h=get(22);
System.out.println(h.name);
}
}
八、DAO----数据访问对象
DAO=Data Access Object
实际上就是运用了ORM中的思路,把数据库相关的操作都封装在这个类里面,其他地方看不到JDBC的代码
package jbbc;
import java.util.List;
import charactor.Hero;
public interface DAO {
public void add(Hero hero);
public void update(Hero hero);
public void delete(Hero hero);
public void get(int id);
public List<Hero> list();
public List<Hero> list(int start,int count);
}
九、数据库连接池(ConnectionPoll)
与线程池类似数据库也有一个数据库连接池,不过实现思路不一样
传统方式:当有多个线程需要连接数据库并执行的话,那么每个线程就会创建一个连接,并且在 使用完毕后关闭连接。这种方式耗时间加上一个数据库同时支持的连接总数是有限的, 如果并发量很大就会卡顿并且后续线程发起连接就会失败。
使用池:与传统方式不同,连接池在使用之前就会创建好一定数量的连接。这个时候如果有线程 需要连接那么就从连接池里借用而不是重新创建。使用完毕后归还供下一个线程使用。 倘若多线程并发,其他线程就会临时等待直到有连接归还。整个过程连接不会关闭一直循环
ConnectionPoll构造方法和初始化
1、ConnectionPool()构造方法约定了这个连接池一共有多少连接
2、在init()初始化方法中,创建了size条连接。注意,这里不能使用 try-with-resourse这种自动 关闭连接的方式,因为连接恰恰需要保持不关闭状态,供后续循环使用
3、getConnection,判断是否为空,如果是空的就wait,否则就借用一条连接出去
4、returnConnection,在使用完毕后,归还这个连接到连接池,并且在归还完毕后调用notify,通 知那些等待的线程,有新的连接可以借用了
package jbbc;
import java.util.List;
import java.util.ArrayList;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionPool {
List<Connection>cp=new ArrayList<Connection>();
int size;
public ConnectionPool(int size){
this.size=size;
init();//初始化calss变量
}
public void init(){
//这里恰恰不能使用try-with-resource的方式,因为这些连接都需要循环,不能被自动关闭了
try{
Class.forName("com.mysql.jdbc.Driver");
for(int i=0;i<size;i++){
Connection c = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root", "admin");
cp.add(c);
}
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}
}
public synchronized Connection getConnection(){
while(cp.isEmpty()){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
Connection c=cp.remove(0);
return c;
}
public synchronized void returnConnection(Connection c){
cp.add(c);
this.notify();
}
}
测试类(初始化3条连接,创建100条线程,拿到连接后执行一个耗时1秒的SQL语句)
package jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import jdbc.ConnectionPool;
public class TestConnectionPool {
public static void main(String[] args) {
ConnectionPool cp = new ConnectionPool(3);
for (int i = 0; i < 100; i++) {
new WorkingThread("working thread" + i, cp).start();
}
}
}
class WorkingThread extends Thread {
private ConnectionPool cp;
public WorkingThread(String name, ConnectionPool cp) {
super(name);
this.cp = cp;
}
public void run() {
Connection c = cp.getConnection();
System.out.println(this.getName()+ ":\t 获取了一根连接,并开始工作" );
try (Statement st = c.createStatement()){
//模拟时耗1秒的数据库SQL语句
Thread.sleep(1000);
st.execute("select * from hero");
} catch (SQLException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
cp.returnConnection(c);
}
}