JDBC二部曲之一_介绍

JDBC

1 什么是JDBC?

JDBC(Java DataBase Connectivity),即Java数据库连接!也就是说,Java程序员可以使用JDBC API来操作数据库。

最早JDBC是Java EE中的规范,但是现在已经添加到Java SE中了。也就是说,JDBC API在JDK中就已经存在了。与JDBC相关的包有:java.sql和javax.sql两个包!

2 JDBC原理

  早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范,并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的范围命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动!

现在大家已经知道了,想使用JDBC必须要有驱动才可以,驱动就是JDBC中接口的实现,并且是针对自己厂商的数据库服务器的实现。我们使用的是MySQL数据库服务器,那么就需要有MySQL驱动。

3.JDBC编码步骤

连接数据库需要MySQL的JDBC驱动,即:mysql-connector-java-5.1.13-bin.jar。

对数据库的操作可以大致分为两种:更新操作、查询操作。更新操作就是增、删、改,它没有结果,而查询操作是有结果的。

  1. A.       注册驱动
  2. B.        获取与数据库的连接
  3. C.       得到代表SQL语句的对象
  4. D.       发送SQL语句:DML、DQL
  5. E.        获取结果集
  6. F.        从结果集中获取数据
  7. G.       关闭

1、注册驱动


Driver d = new Driver();

DriverManager.registerDriver(d);

其中Driver是com.mysql.jdbc.Driver类型,这种方法使用了硬编码,也就是说将来如果想更换数据库是不可以的,因为代码中使用了MySQL提供的类。

其实JDBC为了让我们不出现硬编码,要求各个厂商提供的驱动都要可以把自己来注册到DriverManager中,答案在Driver()的源码中。


static {

try {

java.sql.DriverManager.registerDriver(new Driver());

} catch (SQLException E) {

throw new RuntimeException("Can‘t register driver!");

}

}

上面代码是com.mysql.jdbc.Driver类中的静态代码块,它会new一个自己类型的对象,然后再把自己注册到DriverManager中。我们都知道static块会在类被加载时就会执行,也就是说,在JVM加载Driver类时,已经把一个Driver类的对象注册到DriverManager中了,所以我们就不需要再去注册了。我们只需要保证Driver类被加载就OK了!修改上面加载驱动的代码如下:


Class.forName("com.mysql.jdbc.Driver");

上面代码是用来加载com.mysql.jdbc.Driver类的代码,这样就可以把Driver注册到DriverManager中了!

  在我们的代码中,不要出现驱动Jar包中的类!这方便我们将来切换数据库!!!

2、获取与数据库的连接


//方式一:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day10", "root", "sorry");

//方式二:

Properties props = new Properties();

props.put("user", "root");//key看数据库的规定

props.put("password", "sorry");

props.put("useUnicode", "true");

props.put("characterEncoding", "utf8");

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day10", props);

//方式三:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day10?user=root&password=sorry");

还可以使用DriverManager的registerDriver()方法,但这个方法通常我们是不会使用的,因为使用它会导致对特定驱动的依赖。

注意,数据库的URL需要查看相关文档,不同数据库不同版本,URL都不一定一样.

URL:   协议(jdbc):子协议(mysql):主机:端口/数据库

  我们知道在使用DriverManager的getConnection()方法时需要给出三个参数,其中一个是url,下面我们来聊一聊这个url是什么东西。JDBC使用的URL用于描述数据的来源,它的语法如下:

可以把它分为三个部分,每个部分中间都是冒号:

l  其中第一部分是jdbc,这是固定的;

l  第二部分为子协议名称,一般都是特定厂商的数据库名称,例如MySQL就是jdbc:mysql:…,Oracle就是jdbc:oracle:…;

l  第三部分由数据库厂商来确定,这一部分通常需要说明数据库服务器主机的IP、端口,以及数据库名称。

MySQL的url:jdbc:mysql://localhost:3306/mydb1

其实还可以给url添加参数,最为常见的参数就是:

l  useUnicode=true

l  characterEncoding=UTF8

这两个参数是指定获取的Connection使用的编码!当然如果没有指定,那么会使用当前MySQL数据库的字符编码集。因为我们在安装MySQL时已经指定的UTF8,所以就算不指定这两个参数,获取到的连接也同样是UTF8的。

其实在不指定主机和端口时,默认也是连接localhost主机的3306端口,所以可以把url写成如下的样子:

jdbc:mysql:///mydb1

当然,最后的mydb1是要连接的数据库,这部分是不能少的。

建议把url写完整了:

jdbc:mysql://localhost:3306/mydb1?useUnicode=true&characterEncoding=UTF8

3、得到代表SQL语句的对象

Statement stmt = conn.createStatement();

4、发送SQL语句:DML、DQL

ResultSet rs = stmt.executeQuery("selcet chinese,english,math from student");

//Statement常用方法

//ResultSet executeQuery(String sql):sql一般是DQL语句, 用来发送查询语句.

//int executeUpdate(String sql):sql一般是没有返回结果集的语句,比如DML、DDL。返回值是影响到的行数, 用来发送增、删、改语句

//boolean execute(String sql):sql可以任何的语句。返回值:如果执行的sql语句有结果集,返回true,没有结果集,返回false

5、如果是DQL语句,有结果,得到返回的结果

6、遍历结果集


while(rs.next()){

System.out.println("---------------------------");

System.out.print(rs.getObject("chinese")+"\t");

System.out.print(rs.getObject("english")+"\t");

System.out.println(rs.getObject("math"));

}

7、释放占用的资源(官方文档,最好写成工具类.)

前面我们在获取Connection时使用了硬编码,把driverClassName、url、username、password都直接写到了Java代码中,如果将来需要更换数据库,或者更换用户来登录数据库,这都不方便,所以我们应该把连接数据库的数据写到配置文件中,例如:


//JDBC工具类

public class JdbcUtil {

private static String driverClass;

private static String url;

private static String user;

private static String password;

static{

try {

//从配置文件中读取信息

InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dbcfg.properties");

Properties pro = new Properties();

pro.load(in);

driverClass = pro.getProperty("driverClass");

url = pro.getProperty("url");

user = pro.getProperty("user");

password = pro.getProperty("password");

Class.forName(driverClass);

catch (Exception e) {

throw new ExceptionInInitializerError(e);

}

}

public static Connection getConnection() throws Exception {

return DriverManager.getConnection(url,user,password);

}

public static void release(ResultSet rs,Statement stat,Connection conn){

if(rs!=null){

try {

rs.close();

catch (SQLException e) {

e.printStackTrace();

}

rs = null;

}

if(stat!=null){

try {

stat.close();

catch (SQLException e) {

e.printStackTrace();

}

stat = null;

}

if(conn!=null){

try {

conn.close();

catch (SQLException e) {

e.printStackTrace();

}

conn = null;

}

}

}

3.SQL攻击

在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段!

演示SQL攻击

首先我们需要创建一张用户表,用来存储用户的信息。


CREATE TABLE tab_user(

       uid  CHAR(32) PRIMARY KEY,

       username       VARCHAR(30) UNIQUE KEY NOT NULL,

       PASSWORD        VARCHAR(30)

);

 

INSERT INTO tab_user VALUES(‘U_1001‘, ‘zs‘, ‘zs‘);

SELECT * FROM tab_user;

现在用户表中只有一行记录,就是zs。

下面我们写一个login()方法!


public void login(String username,   String password) {

Connection con = null;

Statement stmt = null;

ResultSet rs = null;

try {

con = JdbcUtils.getConnection();

stmt = con.createStatement();

String sql = "SELECT * FROM tab_user WHERE " +

"username=‘" + username +

"‘ and password=‘" +   password + "‘";

rs = stmt.executeQuery(sql);

if(rs.next()) {

System.out.println("欢迎" + rs.getString("username"));

} else {

System.out.println("用户名或密码错误!");

}

} catch (Exception e) {

throw new RuntimeException(e);

} finally {

JdbcUtils.close(con, stmt,   rs);

}

}

下面是调用这个方法的代码:


login("a‘ or ‘a‘=‘a", "a‘ or ‘a‘=‘a");

这行当前会使我们登录成功!因为是输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起!我们来看看组合在一起的SQL语句:


SELECT * FROM tab_user WHERE username=‘a‘ or ‘a‘=‘a‘ and password=‘a‘ or ‘a‘=‘a

2 防止SQL攻击

过滤用户输入的数据中是否包含非法字符;

分步交验!先使用用户名来查询用户,如果查找到了,再比较密码;

使用PreparedStatement

PreparedStatement是Statement的子接口,你可以使用PreparedStatement来替换Statement。

PreparedStatement的好处:

防止SQL攻击;

提高代码的可读性,以可维护性;

提高效率。(支持预编译SQL –占位符’? ’)

2 PreparedStatement的使用


String sql = “select * from   tab_student where s_number=?”;

PreparedStatement pstmt = con.prepareStatement(sql);

pstmt.setString(1, “S_1001”);

ResultSet rs = pstmt.executeQuery();

rs.close();

pstmt.clearParameters();//再次使用时需要把原来的设置清空。

pstmt.setString(1, “S_1002”);

rs = pstmt.executeQuery();

在使用Connection创建PreparedStatement对象时需要给出一个SQL模板,所谓SQL模板就是有“?”的SQL语句,其中“?”就是参数。

在得到PreparedStatement对象后,调用它的setXXX()方法为“?”赋值,这样就可以得到把模板变成一条完整的SQL语句,然后再调用PreparedStatement对象的executeQuery()方法获取ResultSet对象。

注意PreparedStatement对象独有的executeQuery()方法是没有参数的,而Statement的executeQuery()是需要参数(SQL语句)的。因为在创建PreparedStatement对象时已经让它与一条SQL模板绑定在一起了,所以在调用它的executeQuery()和executeUpdate()方法时就不再需要参数了。

PreparedStatement最大的好处就是在于重复使用同一模板,给予其不同的参数来重复的使用它。这才是真正提高效率的原因。

所以,建议大家在今后的开发中,无论什么情况,都去需要PreparedStatement,而不是使用Statement

4.DAO解耦

把控制权转移到外面,想要创建对象,必须先传参数依赖注入

public void setDao(User dao){

this.dao = dao;

{

另一种方式通过构造函数转过来

单开就是  单例设计模式


//到底使用哪一个,是由自己指定的。是new出来的,没法解耦

//如果由外部传入使用的实现类,这个过程称之为控制反转,就可以解耦了.IoC DI:依赖注入:Spring的核心

//工厂创建模式,就是把创建的细节隐藏起来.

private UserDao dao = DaoFactory.getInstance().getUserDaoImpl();// = new UserDaoMySQLEnhanceImpl();

//    public UserServiceImpl(UserDao dao){//依赖注入方法一,通过构造方式

//        this.dao = dao;

//    }

//    public void setDao(UserDao dao){//依赖注入方式二,通过set方法

//        this.dao = dao;

//    }


//创建DAO实例的工厂:饿汉子式单例

public class DaoFactory {

//先创建工厂类,需要私有化,为了能一家在就实例化,需要静态修饰

private static DaoFactory instance = new DaoFactory();

//无参构造函数私有化

private DaoFactory(){}

//定义静态方法,调用类

public static DaoFactory getInstance(){

return instance;

}

//私有Properties类,

private static Properties props = new Properties();

static{

//静态代码块,初始化就加载,这样就参数就可以使用字符串,就可以写到配置文件中.

InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");

try {

props.load(in);

catch (IOException e) {

throw new RuntimeException(e);

}

}

//创建一个UserDaoImpl实例类,需要的参数读取配置文件

//这样就进行了解耦,所有需要的参数可以通过修改配置文件来完成

public UserDao getUserDaoImpl(){

try {

String daoImplName = props.getProperty("userDao");

return (UserDao)Class.forName(daoImplName).newInstance();

catch (Exception e) {

throw new RuntimeException(e);

}

}

}

5 小知识点

读取二进制文件

我们一直在向数据库保存varhcar、int、date等类型的数据,现在我们要尝试把一张图片或一个mp3保存到数据库中去。

首先我们需要创建一张表,表中要有一个mediumblob(16M)类型的字段。


CREATE TABLE tab_bin(

       id   INT       PRIMARY   KEY AUTO_INCREMENT,

       filename VARCHAR(100),

       data       MEDIUMBLOB

);

  向数据库插入二进制数据需要使用PreparedStatement为原setBinaryStream(int, InputSteam)方法来完成。


con = JdbcUtils.getConnection();

String sql = "insert into tab_bin(filename,data) values(?,   ?)";

pstmt = con.prepareStatement(sql);

pstmt.setString(1, "a.jpg");

InputStream in = new FileInputStream("f:\\a.jpg");//得到一个输入流对象

pstmt.setBinaryStream(2, in);// 为第二个参数赋值为流对象

pstmt.executeUpdate();

读取二进制数据,需要在查询后使用ResultSet类的getBinaryStream()方法来获取输入流对象。也就是说,PreparedStatement有setXXX(),那么ResultSet就有getXXX()。


con = JdbcUtils.getConnection();

String sql = "select filename,data from tab_bin where   id=?";

pstmt = con.prepareStatement(sql);

pstmt.setInt(1, 1);

rs = pstmt.executeQuery();

rs.next();

String filename = rs.getString("filename");

OutputStream out = new   FileOutputStream("F:\\" + filename);

//使用文件名来创建输出流对象。

InputStream in =   rs.getBinaryStream("data");//读取输入流对象

IOUtils.copy(in, out);// 把in中的数据写入到out中。

out.close();

  还有一种方法,就是把要存储的数据包装成Blob类型,然后调用PreparedStatement的setBlob()方法来设置数据


con = JdbcUtils.getConnection();

String sql = "insert into tab_bin(filename,data) values(?, ?)";

pstmt = con.prepareStatement(sql);

pstmt.setString(1, "a.jpg");

File file = new File("f:\\a.jpg");

byte[] datas = FileUtils.getBytes(file);//获取文件中的数据

Blob blob = new SerialBlob(datas);//创建Blob对象

pstmt.setBlob(2, blob);//设置Blob类型的参数

pstmt.executeUpdate();


con = JdbcUtils.getConnection();

String sql = "select filename,data from tab_bin where id=?";

pstmt = con.prepareStatement(sql);

pstmt.setInt(1, 1);

rs = pstmt.executeQuery();

rs.next();

String filename = rs.getString("filename");

File file = new File("F:\\" + filename) ;

Blob blob = rs.getBlob("data");

byte[] datas = blob.getBytes(0, (int)file.length());

FileUtils.writeByteArrayToFile(file,   datas);

批处理SQL

语句不同,有很多行,可以使用addBatch方法批处理,别忘了executeBatch一下.

其实Batch里面就是封装了一个缓存


//向t1表中插入两条记录,删除第一条记录

//利用statement可以执行SQL语句不同的批处理

public void testBatch1(){

Connection conn = null;

Statement stmt = null;

try{

conn = JdbcUtil.getConnection();

stmt = conn.createStatement();

//把要输入的SQL语句放入字符串变量中去;

String sql1 = "insert into t1 (id,name) values(1,‘aaa1‘)";

String sql2 = "insert into t1 (id,name) values(2,‘bbb1‘)";

String sql3 = "delete from t1 where id=1";

//注入SQL语句,是注入,并没有执行;

stmt.addBatch(sql1);

stmt.addBatch(sql2);

stmt.addBatch(sql3);

//对注入的SQL语句执行

//数组的元素表示每条语句影响到的行数。

int[] i = stmt.executeBatch();

}catch(Exception e){

throw new RuntimeException(e);

}finally{

JdbcUtil.release(null, stmt, conn);

}

}

利用PreparedStatement执行批处理,只能用在SQL语句相同的情况下。参数有可能不同


//向t1表中批量插入10条记录,语句相同,参数不同

@Test

public void testBatch2(){

Connection conn = null;

PreparedStatement stmt = null;

try{

conn = JdbcUtil.getConnection();

//一开始就需要把SQL语句加载到PreparedStatement中,用占位符

String sql="insert into t1 (id,name) values(?,?)";

stmt = conn.prepareStatement(sql);

//输入SQL语句中的参数

for(int x=0;x<10;x++){

stmt.setInt(1, x+1);

stmt.setString(2, "aaa"+(x+1));

//对参数进行注入

stmt.addBatch();

}

//对注入的参数执行

stmt.executeBatch();

}catch(Exception e){

throw new RuntimeException(e);

}finally{

JdbcUtil.release(null, stmt, conn);

}

}

OCI  效率高  但是要装客户文件

Thin 效率低 但是不需要装

一般开发用这个Thin

插入1000001条记录,大数据插入


//向t1表中批量插入1000001条记录

@Test

public void testBatch3(){

Connection conn = null;

PreparedStatement stmt = null;

try{

conn = JdbcUtil.getConnection();

//一开始就需要把SQL语句加载到PreparedStatement中,用占位符

String sql="insert into t1 (id,name) values(?,?)";

stmt = conn.prepareStatement(sql);

//输入SQL语句中的参数

for(int x=0;x<1000001;x++){

stmt.setInt(1, x+1);

stmt.setString(2, "aaa"+(x+1));

stmt.addBatch();

//定义一个判断,每插入1000条就执行一次,还需要清空注入的语句.

if(x%1000==0){

stmt.executeBatch();//执行

stmt.clearBatch();//清空

}

}

//扫尾执行,因为可能会有尾数

stmt.executeBatch();

}catch(Exception e){

throw new RuntimeException(e);

}finally{

JdbcUtil.release(null, stmt, conn);

}

}

返回自增长主键

当表的主键是自增长的,那么就是由数据库来维护主键的值。当我们使用Java向数据库插入一行记录后,主键的值我们是不知道的。当然你可以去再查询一次!但是这不是最好的办法。

想获取主键自增长的值,需要在创建PreparedStatement时开始:


String sql = "insert into tt2(name) values(?)";

pstmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

//表示在执行SQL语句后会去获取主键自增长的值。

pstmt.setString(1,   "hello");

pstmt.executeUpdate();

在执行完INSERT语句之后,可以通过PrepareStatement的getGeneratedKeys()方法获取一个结果集对象,然后再获取结果集中唯一一行,唯一一列的数据,这就是主键自增长的值。


ResultSet rs =   pstmt.getGeneratedKeys();//获取自增长主键结果集

rs.next();

int id = rs.getInt(1);

System.out.println(id);

调用存储过程

1 创建存储过程

首先创建3个存在过程:

l  无参的;

l  入参的;

l  出参的。


DELIMITER //

CREATE PROCEDURE pro1()

BEGIN

SELECT   * FROM stu;

END//

DELIMITER ;


DELIMITER //

CREATE PROCEDURE pro2(IN _sid   VARCHAR(10))

BEGIN

SELECT * FROM stu WHERE sid=_sid;

END//

DELIMITER ;


DELIMITER //

CREATE PROCEDURE pro3(IN _sid   VARCHAR(10), OUT _sname VARCHAR(20))

BEGIN

SELECT sname INTO _sname FROM stu WHERE sid=_sid;

END//

DELIMITER ;

2 调用pro1(无参存储过程)

要调用存储过程需要使用CallableStatement,获取CallableStatement需要调用Connection的prepareCall(String sql)方法。


con = JdbcUtils.getConnection();

String sql = "{call pro1()}";//调用存储过程的SQL语句需要一对大括号括起来

cstmt = con.prepareCall(sql);

rs = cstmt.executeQuery();//如果存储过程会返回结果集,那么就调用executeQuery()方法,否则调用executeUpdate()方法。

while(rs.next()) {

int colCnt =   rs.getMetaData().getColumnCount();

for(int i = 1; i <= colCnt; i++) {

Object o = rs.getObject(i);

System.out.print(o + " ");

}

System.out.println();

}

3 调用有入参存储过程


con = JdbcUtils.getConnection();

String sql = "{call pro2(?)}";//给出一个参数

cstmt = con.prepareCall(sql);

cstmt.setString(1, "S_100");//为参数赋值

rs = cstmt.executeQuery();

rs.next();

System.out.println(rs.getString(1) + ", " + rs.getString(2)

+   ", " + rs.getInt(3) + ", " + rs.getString(4));

4 调用有出参的存储过程


con = JdbcUtils.getConnection();

String sql = "{call   pro3(?,?)}";//第一个参数是入参,第二个是出参。

cstmt = con.prepareCall(sql);

cstmt.setString(1, "S_100");

cstmt.registerOutParameter(2, Types.VARCHAR);// 注册一个出参!说明它的SQL类型

cstmt.execute();//这里调用executeUpdate()、executeQuery()或是execute()都行!

String name = cstmt.getString(2);// 获取出参的值

System.out.println(name);

元数据

1 DatabaseMetaData

获取DatabaseMetaData对象:


Connection con =   ...

DatabaseMetaData   dbmd = con.getMetaData();

基本功能


String name =   dbmd.getDatabaseProductName();//获取数据库名称:MySQL

int v1 = dbmd.getDatabaseMajorVersion();//获取数据库主版本号:5

int v2 = dbmd.getDatabaseMinorVersion();//获取数据库次版本号:1

System.out.println(name + v1 + "." + v2);


String driverName   = dbmd.getDriverName();//获取驱动名

String   driverVersion = dbmd.getDriverVersion();//获取驱动版本

System.out.println(driverName   + ", " + driverVersion);


String url =   dbmd.getURL();//获取URL

String username =   dbmd.getUserName();//获取用户名

System.out.println(url);

System.out.println(username);

获取所有数据库名称


ResultSet rs =   dbmd.getCatalogs();

while(rs.next()) {

System.out.println(rs.getString(1));

}

获取指定数据库中所有表名称


ResultSet rs =   dbmd.getTables("mydb1", null, null, new String[]{"TABLE"});

while(rs.next())      {

System.out.println(rs.getString("TABLE_NAME"));

}

2 ParameterMetaData

得到ParameterMataData对象


Connection con = ...

String sql = "insert into tab_student value(?,?,?,?)";

PreparedStatement   pstmt = con.prepareStatement(sql);

ParameterMetaData pmd =   pstmt.getParameterMetaData();

ParameterMetaData是针对“?”的元数据!但是很多驱动对它的支持不是很好。


int cnt = pmd.getParameterCount();//参数的个数

for(int i = 1; i <= cnt; i++) {

System.out.println(pmd.getParameterTypeName(i));//当前参数类型名

System.out.println(pmd.getParameterType(i));//当前参数类型

System.out.println(pmd.getParameterClassName(i));//当前参数Java类型名

System.out.println(pmd.isNullable(i));//当前参数是否可以为NULL

}

3 结果集列数据:ResultSetMetaDate

获取ResultSetMetaData


ResultSet rs = ...;

ResultSetMetaData rsmd = rs.getMetaData();

方法介绍:


ResultSetMetaData rsmd =   rs.getMetaData();

int cnt = rsmd.getColumnCount();//获取结果集列数

for(int i = 1; i <= cnt; i++) {

System.out.print(rsmd.getColumnName(i));//获取当前列名称

if(i < cnt) {

System.out.print(", ");

}

}

System.out.println();

for(int i = 1; i <= cnt; i++) {

System.out.print(rsmd.getColumnClassName(i));//获取当前列Java类型名

if(i < cnt) {

System.out.print(", ");

}

}

System.out.println();

while(rs.next()) {

for(int i = 1; i <= cnt; i++) {

System.out.print(rs.getObject(i));//看清楚,这个是rs不是rsmd

if(i < cnt) {

System.out.print(", ");

}

}

System.out.println();

}

时间: 2024-08-24 11:54:45

JDBC二部曲之一_介绍的相关文章

JDBC二部曲之_事物、连接池

事务 事务概述 事务的四大特性(ACID) 事务的四大特性是: l  原子性(Atomicity):事务中所有操作是不可再分割的原子单位.事务中所有操作要么全部执行成功,要么全部执行失败. l  一致性(Consistency):事务执行后,数据库状态与其它业务规则保持一致.如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的. l  隔离性(Isolation):隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰. l  持久性(Durabil

Struts2.3.15+Hibernate 4.x+Spring4.x 整合二部曲之上部曲

1 导入jar包 可以复制jar包或maven导入,本文最后会给出github地址 2 导入log4j.properties文件 og4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.err log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.Co

Python基础知识两部曲:二

如果没有看基础部分第一章,请前往Python基础知识两部曲:一 8.函数 1.定义函数: 使用关键字def来告诉python你要定义一个函数 接着指出函数名:如下面函数名是--greet_user ()是必须带上的,这里可以可以传递一些参数,也可以不传 以:结尾,且与后面所有的缩进构成了函数体 调用函数直接写上函数名,如果有参数记得带上参数 1. 无参数的函数: def greet_user(): """显示简单的函数体""" print(&qu

Linux平台上SQLite数据库教程(二)——C语言API介绍

Linux平台上SQLite数据库教程(二)--C语言API介绍 前言:本文将介绍几个基本的SQLite3数据库的C语言API接口,主要用到两个文件:sqlite3.c.sqlite3.h.源码地址:https://github.com/AnSwErYWJ/SQLite. 打开数据库 1.原型: int sqlite3_open( const char* filename, /* 数据库文件名, 必须为 UTF-8 格式 */ sqlite3** ppDB /* 输出: SQLite 数据库句柄

攻城狮在路上(贰) Spring(二)--- Spring IoC概念介绍

一.IoC的概念: IoC(控制反转)是Spring容器的核心.另一种解释是DI(依赖注入),即让调用类对某一个接口的依赖关系由第三方注入,以移除调用类对某一个接口实现类的一览. 定义如此,由此可见,在面向接口编程的情况下,IoC可以很好的实现解耦,可以以配置的方式为程序提供所需要的接口实现类. 在实际程序开发中,我们只需要提供对应的接口及实现类,然后通过Spring的配置文件或者注解完成对依赖类的装配.二.IoC的类型: 1.通过构造函数: 此种方式的缺点是,在构造函数中注入之后一般会作为一个

进击的Python【第十二章】:mysql介绍与简单操作,sqlachemy介绍与简单应用

进击的Python[第十二章]:mysql介绍与简单操作,sqlachemy介绍与简单应用 一.数据库介绍 什么是数据库? 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,每个数据库都有一个或多个不同的API用于创建,访问,管理,搜索和复制所保存的数据.我们也可以将数据存储在文件中,但是在文件中读写数据速度相对较慢.所以,现在我们使用关系型数据库管理系统(RDBMS)来存储和管理的大数据量.所谓的关系型数据库,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来

跟老杨学java系列(二) PNP训练法介绍

跟老杨学java系列(二) PNP训练法介绍 声明:以下内容可能会有做广告的嫌疑,哈哈~~.大家不想看的忽略就好..欢迎大家留言探讨技术问题.其他问题概不回复. (书接上回)PNP训练法是国内java培训机构-乐橙谷推出的一种针对java培训的学习训练方法.该训练法摒弃了传统的以章节知识点为线索的教学方式,改为以完整的项目开发过程为线索.以对功能点的分析设计驱动出的知识点为授课内容.以迭代为教学手段,学习难度由浅入深,让学生在充分了解软件开发过程的基础上,熟练掌握项目开发经验.深入掌握相关开发技

计划在CSDN学院推出系列视频课程《源码分析教程5部曲》

?? 计划在CSDN学院推出系列视频课程<源码分析教程5部曲> 源码分析教程5部曲之1--漫游C语言 源码分析教程5部曲之2--C标准库概览 源码分析教程5部曲之3--libevent源码分析 源码分析教程5部曲之4--memcached源码分析 源码分析教程5部曲之5--redis源码分析

监控利器Nagios之二:Nagios的细致介绍和监控外部服务器的私有信息

监控利器Nagios之二:Nagios的细致介绍和监控外部服务器的私有信息 Nagios的监控对象的定义和对被监控设备的动作命令是分开的.一个是主程序包,一个是插件包.先来说说nagios主程序nagios-plugins插件和nrpe软件的用处, Nagios主程序: Nagios主程序提供一个的只是一个监控平台,真正用作工作的并不是它,而是他的插件.在Nagios 安装完毕后,默认的配置文件在/usr/local/nagios/etc目录下.里面定义了nagios能调用的命令.对打印机和交换