JDBC

JDBC (Java DataBase Connection) 指通过JAVA访问数据库。

package jdbc;
  
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
  
public class TestJDBC {
    public static void main(String[] args) {
  
//初始化驱动类com.mysql.jdbc.Driver


        try { Class.forName("com.mysql.jdbc.Driver").newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
  //建立与数据库的Connection连接(前提MySQL中已经创建了数据库)
这里需要提供:
数据库所处于的ip:127.0.0.1 (本机)
数据库的端口号: 3306 (mysql专用端口号)
数据库名称 how2java
编码方式 UTF-8
账号 root
密码 admin
        try (
            Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
                "root", "admin");
//Statement是用于执行SQL语句的(使用java.sql.Statement),比如增加,删除 
            Statement s = c.createStatement();              
        )
        {
            String sql = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";
            s.execute(sql);
              
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

 使用try-with-resource的方式自动关闭连接,因为Connection和Statement都实现了AutoCloseable接口 。

CRUD是最常见的数据库操作,即增删改查
C 增加(Create)
R 读取查询(Retrieve)
U 更新(Update)
D 删除(Delete)

在JDBC中增加,删除,修改的操作都很类似,只是传递不同的SQL语句就行了。

删除和增加很类似,只不过是执行的SQL语句不一样罢了    String sql = "delete from hero where id = 5";

修改也一样,执行另一条SQL语句就可以了    String sql = "update hero set name = 'name 5' where id = 3";

查询:executeQuery 执行SQL查询语句

注意: 在取第二列的数据的时候,用的是rs.get(2) ,而不是get(1). 这个是整个Java自带的api里唯二的地方,使用基1的,即2就代表第二个。

另一个地方是在PreparedStatement这里

String sql = "select * from hero";

 

            // 执行查询语句,并把结果集返回给ResultSet

            ResultSet rs = s.executeQuery(sql);

            while (rs.next()) {

                int id = rs.getInt("id");// 可以使用字段名

                String name = rs.getString(2);// 也可以使用字段的顺序

                float hp = rs.getFloat("hp");

                int damage = rs.getInt(4);

                System.out.printf("%d\t%s\t%f\t%d%n", id, name, hp, damage);

            }

            // 不一定要在这里关闭ReultSet,因为Statement关闭的时候,会自动关闭ResultSet

            // rs.close();

和Statement一样,预编译PreparedStatement也是用来执行sql语句的,与创建Statement不同的是,需要根据sql语句创建PreparedStatement
除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。

和ResultSet,都是基1的。

package jdbc;

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


public class TestJDBC {
	public static void main(String[] args) {
			try {
					Class.forName("com.mysql.jdbc.Driver").newInstance();
				} catch (InstantiationException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (IllegalAccessException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (ClassNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			
                            //先创建sql语句
                                String sql = "insert into hero values(null,?,?,?)";

				try(Connection c= DriverManager.getConnection(
						"jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8",
				        "root", "admin");
						// 根据sql语句创建PreparedStatement
				        PreparedStatement ps =c.prepareStatement(sql);
						)
				{
	                // 设置参数
				ps.setString(1, "提莫");
	                        ps.setFloat(2, 313.0f);
	                        ps.setInt(3, 50);
				ps.execute();
					
						
				}catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
	            } 
                }
	    }

PreparedStatement的优点1-参数设置

 

PreparedStatement的优点2-性能表现

      PreparedStatement有预编译机制,性能比Statement更快 。

Statement执行10次,需要10次把SQL语句传输到数据库端,数据库要对每一次来的SQL语句进行编译处理

 

PreparedStatement 执行10次,只需要1次把SQL语句传输到数据库端,数据库对带?的SQL进行预编译
每次执行,只需要传输参数到数据库端
    1. 网络传输量比Statement更小
    2. 数据库不需要再进行编译,响应更快

 

PreparedStatement的优点3-防止SQL注入式攻击

假设name是用户提交来的数据

String name = "'盖伦' OR 1=1";


使用Statement就需要进行字符串拼接
拼接出来的语句是:

select * from hero where name = '盖伦' OR 1=1


因为有OR 1=1,这是恒成立的
那么就会把所有的英雄都查出来,而不只是盖伦,并且暴露所有信息
如果Hero表里的数据时海量的,比如几百万条,把这个表里的数据全部查出来
会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢

而PreparedStatement使用的是参数设置,就不会有这个问题

package jdbc;
  
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
  
public class TestJDBC {
    public static void main(String[] args) {
  
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
  
        String sql = "select * from hero where name = ?";
        try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin");
                Statement s = c.createStatement();
            PreparedStatement ps = c.prepareStatement(sql);
        ) {
            // 假设name是用户提交来的数据
            String name = "'盖伦' OR 1=1";
            String sql0 = "select * from hero where name = " + name;
            // 拼接出来的SQL语句就是
            // select * from hero where name = '盖伦' OR 1=1
            // 因为有OR 1=1,所以恒成立
            // 那么就会把所有的英雄都查出来,而不只是盖伦
            // 如果Hero表里的数据时海量的,比如几百万条,把这个表里的数据全部查出来
            // 会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢
            System.out.println(sql0);
  
            ResultSet rs0 = s.executeQuery(sql0);
            while (rs0.next()) {
                String heroName = rs0.getString("name");
                System.out.println(heroName);
            }
  
            s.execute(sql0);
  
            // 使用预编译Statement就可以杜绝SQL注入
  
            ps.setString(1, name);
  
            ResultSet rs = ps.executeQuery();
            // 查不出数据出来
            while (rs.next()) {
                String heroName = rs.getString("name");
                System.out.println(heroName);
            }
 
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  
    }
}

1.Statement、PreparedStatement和CallableStatement都是接口(interface)。 
2.Statement继承自Wrapper、PreparedStatement继承自Statement、CallableStatement继承自PreparedStatement。 
3. 
Statement接口提供了执行语句和获取结果的基本方法; 
PreparedStatement接口添加了处理 IN 参数的方法; 
CallableStatement接口添加了处理 OUT 参数的方法。 
4. 
a.Statement: 
普通的不带参的查询SQL;支持批量更新,批量删除; 
b.PreparedStatement: 
可变参数的SQL,编译一次,执行多次,效率高; 
安全性好,有效防止Sql注入等问题; 
支持批量更新,批量删除; 
c.CallableStatement: 
继承自PreparedStatement,支持带参数的SQL操作; 
支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持;   

execute与executeUpdate

1.相同点

都可以执行增加,删除,修改

2.不同点

不同1:
execute可以执行查询语句
然后通过getResultSet,把结果集取出来
executeUpdate不能执行查询语句

不同2:
execute返回boolean类型,true表示执行的是查询语句,false表示执行的是insert,delete,update等等
executeUpdate返回的是int,表示有多少条数据受到了影响

关于

方法executeQuery          

         用于产生单个结果集的语句,例如 SELECT 语句。 被使用最多的执行 SQL 语句的方法是 executeQuery。这个方法被用来执行 SELECT 语句,它几乎是使用最多的 SQL 语句。

 

 

 

获取自增长id

在Statement通过execute或者executeUpdate执行完插入语句后,MySQL会为新插入的数据分配一个自增长id,(前提是这个表的id设置为了自增长,在Mysql创建表的时候,AUTO_INCREMENT就表示自增长)

CREATE TABLE hero (

id int(11) AUTO_INCREMENT,

...

}

数据库知道自增长是多少,但是无论是execute还是executeUpdate都不会返回这个自增长id是多少。需要通过Statement的getGeneratedKeys获取该id
注: 第20行的代码,后面加了个Statement.RETURN_GENERATED_KEYS参数,以确保会返回自增长ID。 通常情况下不需要加这个,有的时候需要加,所以先加上,保险一些

package jdbc;
   
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
   
public class TestJDBC {
   
    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
         String sql = "insert into hero values(null,?,?,?)";
        try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin");
                PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);           
                ) {
  
            ps.setString(1, "盖伦");
            ps.setFloat(2, 616);
            ps.setInt(3, 100);
   
            // 执行插入语句
            ps.execute();
   
            // 在执行完插入语句后,MySQL会为新插入的数据分配一个自增长id
            // JDBC通过getGeneratedKeys获取该id
            ResultSet rs = ps.getGeneratedKeys();
            if (rs.next()) {
                int id = rs.getInt(1);
                System.out.println(id);
            }
   
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
   
    }
}

获取表的元数据

元数据概念:
和数据库服务器相关的数据,比如数据库版本,有哪些表,表有哪些字段,字段类型是什么等等。

package jdbc;
  
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
  
public class TestJDBC {
  
    public static void main(String[] args) throws Exception {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
 
        try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8","root", "admin");) {
  
            // 查看数据库层面的元数据
            // 即数据库服务器版本,驱动版本,都有哪些数据库等等
  
            DatabaseMetaData dbmd = c.getMetaData();
  
            // 获取数据库服务器产品名称
            System.out.println("数据库产品名称:\t"+dbmd.getDatabaseProductName());
            // 获取数据库服务器产品版本号
            System.out.println("数据库产品版本:\t"+dbmd.getDatabaseProductVersion());
            // 获取数据库服务器用作类别和表名之间的分隔符 如test.user
            System.out.println("数据库和表分隔符:\t"+dbmd.getCatalogSeparator());
            // 获取驱动版本
            System.out.println("驱动版本:\t"+dbmd.getDriverVersion());
  
            System.out.println("可用的数据库列表:");
            // 获取数据库名称
            ResultSet rs = dbmd.getCatalogs();
  
            while (rs.next()) {
                System.out.println("数据库名称:\t"+rs.getString(1));
            }
  
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  
    }
}

使用事务

在Mysql中,只有当表的类型是INNODB的时候,才支持事务,所以需要把表的类型设置为INNODB,否则无法观察到事务.

在事务中的多个操作,要么都成功,要么都失败
通过 c.setAutoCommit(false);关闭自动提交
使用 c.commit();进行手动提交
在22行-35行之间的数据库操作,就处于同一个事务当中,要么都成功,要么都失败
所以,虽然第一条SQL语句是可以执行的,但是第二条SQL语句有错误,其结果就是两条SQL语句都没有被提交。 除非两条SQL语句都是正确的。

 

 

ORM

即Object Relationship Database Mapping

对象和关系数据库的映射

简单说,一个对象,对应数据库里的一条记录 

根据ORM的思想,设计其他几个常见的ORM方法:

1.把一个Hero对象插入到数据库中

public static void add(Hero h)


2.把这个Hero对象对应的数据删除掉

public static void delete(Hero h)


3.更新这条Hero对象

public static void update(Hero h)


4.把所有的Hero数据查询出来,转换为Hero对象后,放在一个集合中返回

public static List<Hero> list();

package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import charactor.Hero;

public class TestJDBC2 {
	//提供方法get(int id)
	//返回一个Hero对象 
	public static Hero get(int id) {
		Hero hero = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		try(Connection c =DriverManager.getConnection
				("jdbc:mysql://127.0.0.1:3306/cxyjava?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(3);
				int damage =rs.getInt(4);
				hero.name= name;
				hero.hp = hp;
				hero.damage = damage;
				hero.id = id;
				
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return hero;
	}
	
	//把一个Hero对象插入到数据库中
	public static void add(Hero h) {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//预处理
		String sql = "insert into hero values(null,?,?,?)";
		try(Connection c =DriverManager.getConnection
("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8","root", "admin");
		PreparedStatement ps = c.prepareStatement(sql);)
		{   
			ps.setString(1, h.name);
            ps.setFloat(2, h.hp);
            ps.setInt(3, h.damage);
   
            ps.execute();
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
			//把这个Hero对象对应的数据删除掉
	public static void delete(Hero h) { 
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//预处理
		String sql = "delete from hero where id =?";
		try(Connection c =DriverManager.getConnection
("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8","root", "admin");
		PreparedStatement ps = c.prepareStatement(sql);)
		{   
			ps.setInt(1, h.id);
            ps.execute();
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	//更新这条Hero对象
	public static void update(Hero h) {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String sql = "update hero set name = ?,hp =?,damage = ? where id =?";
		 try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8",
	                "root", "admin");
				 PreparedStatement ps = c.prepareStatement(sql);)
		 { // 设置参数
	            ps.setString(1, h.name);
	            ps.setFloat(2, h.hp);
	            ps.setInt(3, h.damage);
	            ps.setInt(4, h.id);
	   
	            ps.execute();
	   	 
		 } catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
		 //把所有的Hero数据查询出来,转换为Hero对象后,放在一个集合中返回
		 public static List<Hero> list(){
			List <Hero>heros = new ArrayList<>();
			try {
				Class.forName("com.mysql.jdbc.Driver");
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try (Connection c = DriverManager.getConnection
	("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8","root", "admin");
			Statement s = c.createStatement();)
			{
				String sql ="select *from hero";
				ResultSet rs = s.executeQuery(sql);
				
				while(rs.next()) {
					Hero h = new Hero();
					int id = rs.getInt(1);
					String name = rs.getString(2);
	                float hp = rs.getFloat("hp");
	                int damage = rs.getInt(4);
	                h.name = name;
	                h.hp = hp;
	                h.damage = damage;
	                h.id = id;
	                heros.add(h);
				}
				
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
				return heros;
			}
		 
		 public static void main(String[] args) {
			 /*
			  * list()表示返回数据库中所有的Hero记录
  				List<Hero> hs =list();
				第一次调用的时候,hs引用就指向了这个装有 全部Hero对象的集合
				接下来,做了一次增加add(),那么hs引用所指向的集合,就不再是数据库中全部的Hero对象集合了
				所以需要重新执行一遍hs =list(); 使得hs重新指向一个有全部Hero对象的集合
   
			  */
			 List<Hero> hs =list();
		        System.out.println("数据库中总共有" + hs.size() + " 条数据");
		        Hero h = new Hero();
		        h.name = "新的英雄";
		        System.out.println("新加一条数据");
		        add(h);
		        hs =list();
		        System.out.println("数据库中总共有" + hs.size() + " 条数据");
		        System.out.println("取出id=2的数据,它的name是:");
		        h = get(2);
		        System.out.println(h.name);
		        System.out.println("把名字改为 国安,并且更新到数据库");
		        h.name="国安";
		        update(h);
		        System.out.println("取出id=2的数据,它的name是:");
		        h = get(2);
		        System.out.println(h.name);
		        System.out.println("删除id=2的数据");
		        delete(h);
		        hs =list();
		        System.out.println("数据库中总共有" + hs.size() + " 条数据");
		           
		    }
		       
		}











		 
		 
	
		
		
		
		
		
		
	
	
	
		

 

DAO接口 

DAO=DataAccess Object

数据库访问对象

实际上就是运用了ORM中的思路,把数据库相关的操作都封装在这个类里面,其他地方看不到JDBC的代码

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值