JDBC(Java Data Base Connectivity)java数据库链接,由一些接口和类构成的API,主要由java.sql和javax.sql包组成
链接数据的步骤:
- 注册驱动(只做一次):一个线程只能注册一次,在真实环境里应用程序和数据库不在一台机子上,就像是在河的两边。驱动就是一个物流公司
- 建立连接(socket):Connection就是物流公司在桥上建立的一座桥,造桥成本很大。
- 创建执行语句的对象Statement:Statement就是在桥上来回运送货物的汽车。货物就是sql语句
- 执行语句
- 处理执行结果ResultSet:驱动会将结果包装成一个二维表的样子,使用next()方法读取下一行
- 释放资源:释放资源的顺序为ResultSet、Statement、Connection,后创建的要先释放。
//注册驱动,通过反射获得mysql数据库的驱动类,这个类是mysql公司写的,不是sun公司写的在真实的环境里我们写的应用程序和数据库不再一台机子上,所以需要建立连接,底层
//是通过Tcp/ip协议里的socket建立连接的
Class.forName("com.mysql.jdbc.Driver");
/*
* 创建链接,通过驱动管理创建对具体某个数据库链接
* getConnection(url,username,pawwword)
* url格式 jdbc:子协议:子名称//主机号:端口号/数据库名?属性名=属性值&...mysql没有子名称所以不需要写
* 在url后面添加如下内容jdbc:mysql://localhost:3306/db3?useUnicode=true&characterEncoding=UTF-8可以解决乱码问题
* username=“root”
*/
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3","root", "root");
//获得处理数据的statement对象,java是通过这个对象发送语句给数据库的
Statement stat = conn.createStatement();
//执行sql语句,DDL语句使用execute()方法,执行DML语句使用executeUpdata()方法,执行DQl语句使用executeQuery()方法
stat.execute("insert into jdbc1 values(null,'张三')");
//关闭资源,
stat.close();
conn.close();
这里需要注意的有以下几点:
1)执行sql语句的方法有下面三个方法
- boolean execute(sql)执行DDL语句,返回值为boolean类型,true代表有结果集,false代表没有结果集
- int executeUpdate(sql)执行DML语句,返回值为int类型表示生效的行数
- ResultSet executeQuery(sql)执行DQL语句,返回值为ResultSet对象,这个对象是语句的结果集
2)statement、preparedStatement和CallableStatement三者之间的区别(面试常考)
三者都是查询sql的对象,Statement普通查询,PreparedStatement参数化查询对象,CallableStatement存储过程中查询
- statement:系统只执行一次语句时,用statement来执行,因为如果使用preparedStatement对象系统会将sql语句进行预编译,开销比较大,若只执行一次的语句用preparedStatement不合算。通过connection.createStatement()得到
- PreparedStatement:参数化查询,通过connection.preparedStatement(sql)得到,如果数据哭支持的话,系统会对sql语句进行预编译。预编译就是JDBC在创建prepared对象的时候就将sql语句发送给数据库进行编译。如果需要动态的发送sql语句时最好使用preparedStatement,好处哟亮点(1)提高效率(2)避免了sql注入。何为sql注入?就是将一个一条sql语句利用字符串拼接起来的形式,拼接的变量可能会导致sql语句改变原有的意思,也就是说会有恶意sql语法出现如下语句
String sql = "SELECT * FROM tb1_students WHERE name= '"+varname+"' and passwd='"+varpasswd+"'";
如果我们把['or '1' = '1]作为varpasswd传入进来。用户名随意,看看会成为什么? SELECT * FROM tb1_students = '随意'and passwd = '' or '1' = '1'; 因为'1'='1'肯定成立,所以可以任何通过验证。更有甚者: 把[';DROP tabletb1_students;]作为varpasswd传入进来,则: SELECT * FROM tb1_students = '随意' and passwd ='';DROP table tb1_students;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行。而如果你使用预编译语句,你传入的任何内容就不会和原来的语句发生任何匹配的关系,(前提是数据库本身支持预编译,但目前可能没有什么服务端数据库不支持编译了,只有少数的桌面数据库,就是直接文件访问的那些)只要全使用预编译语句,你就用不着对传入的数据做任何过虑。而如果使用普通的statement,有可能要对DROP等做费尽心机的判断和过虑。
创建配置文件
创建配置文件是为了方便客户修改url、uesrname、password这些信息,以下是一个基本的配置文件内容
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/jdbc1?useUnicode=true&characterEncoding=UTF-8 username=root password=123456 initSize=3 maxSize=5
在java开发中需要吧一些极易改变的配置信息放在XML配置文件或者properties配置文件中,读取XML文件内容需要用到dom4j,比较麻烦。相比之下读取properties文件就很方便
读取properties文件的方法
properties类继承于HashTable并实现了Map接口,所以它也是一个键值映射表,不过properties类又一个特殊的地方,他的键值对都是字符串。
properties类主要有四个方法
1)load(InputStream ips)这个方法可以从.properties属性文件对应的文件输入流中,加载属性列表到Properties类对象
2)store(OutputStream out,String comments)这个方法将Properties类对象的属性列表保存到输出流中Properties pro = new Properties(); FileInputStream in = new FileInputStream("a.properties"); pro.load(in); in.close();
FileOutputStream oFile = new FileOutputStream(file, "a.properties");
pro.store(oFile, "Comment");
oFile.close();
3)getProperty/setProperty 这两个方法分别是获取和修改properties对象的属性值
下面介绍几种读取properties文件的方法
首先了解一下properties文件里面键值对的特点
1,键值对格式
2,=等号后面,值前面,的空格,会自动忽略掉
3,值后面的空格,不会忽略
4,=等号后面的双引号,不会忽略
5,#井号后面内容,为注释,忽略
1)基于ClassLoader读取配置文件,该方法只能读取该路径上的配置文件
Properties properties = new Properties();
// 使用ClassLoader加载properties配置文件生成对应的输入流
InputStream in = PropertiesMain.class.getClassLoader().getResourceAsStream("config/config.properties");
// 使用properties对象加载输入流
properties.load(in);
//获取key对应的value值
properties.getProperty(String key);
2)基于InputStream读取配置文件,该方法可以读取任意路径下的配置文件
Properties properties = new Properties();
// 使用InPutStream流读取properties文件
BufferedReader bufferedReader = new BufferedReader(new FileReader("E:/config.properties"));
properties.load(bufferedReader);
// 获取key对应的value值
properties.getProperty(String key);
3)通过 java.util.ResourceBundle 类来读取,这种方式比使用 Properties 要方便一些。
这个类提供软件国际化的捷径。通过此类,可以使您所编写的程序可以:
轻松地本地化或翻译成不同的语言
一次处理多个语言环境
以后可以轻松地进行修改,支持更多的语言环境
说的简单点,这个类的作用就是读取资源属性文件(properties),然后根据.properties文件的名称信息(本地化信息),匹配当前系统的国别语言信息(也可以程序指定),然后获取相应的properties文件的内容。
使用这个类,要注意的一点是,这个properties文件的名字是有规范的:一般的命名规范是:自定义名语言代码国别代码.properties,如果是默认的,直接写为:自定义名.properties
比如:
myres_en_US.properties
myres_zh_CN.properties
myres.properties
//方式一
ResourceBundle rb = ResourceBundle.getBundle("文件名前缀", Locale.getDefault());
System.out.println(rb.getObject("version"));
//方式二
InputStream is = new BufferedInputStream(new FileInputStream("文件名前缀"));
ResourceBundle rbs = new PropertyResourceBundle(is);
System.out.println(rbs.getObject("version"));
DBCP数据库连接池 Data Base Connection Pool为什么使用:如果没有连接池,每一次业务都需要和数据库服务器建立一次连接,业务处理完断开连接,如果有上万次业务就会有上万次的开关连接,频繁开关连接非常浪费资源,使用数据库连接池,可以设置几个初始连接,如果有业务需要使用连接,则从连接池中直接获取,如果连接池中连接用光,则会等待连接归还后再获取连接,一下是建立连接池的一般步骤
BasicDataSource dataSource=new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/db3");
dataSource.setUsername("root");
dataSource.setPassword("root");
//3. 设置连接池策略信息
dataSource.setInitialSize(3);//初始连接数量
dataSource.setMaxActive(5);//最大连接数量
//4.从连接池中获取连接对象
Connection conn = dataSource.getConnection();
System.out.println(conn);