day17-反射-事务

来源:互联网 发布:男友那个很大 知乎 编辑:程序博客网 时间:2024/06/03 19:02

反射

反射的目的:为了操作某个类的属性或方法

反射能够获得:  1.类的构造器   2.属性   3. 方法   

类的加载器将class文件加载到虚拟机中(内存中),那么有一个Class对象(代表是class文件加载到内存后所形成的一个对象)

第一步:获得Class对象(3种办法),

第二步:获得不同参数个数的各个构造器, 就可以对应创建类的实例了.

第三步:获得操作类中的属性.

第四步:获得类中的方法

 

反射的详细的执行过程(4步)



 

* 获得代表这个类加载到内存的字节码文件的对象Class对象.

         public void demo1() throwsClassNotFoundException{

                   // 一种:通过类名.class

                   Classclazz1 = Person.class;

                   // 二种:通过类的实例对象.getClass()方法获得.

                   Personperson =new Person();

                   Classclazz2 =person.getClass();

                   // 三种:使用ClassforName的静态方法获得.(推荐使用这种)

                   Classclazz3 = Class.forName("com.itheima.demo3.reflect.Person");

         }

        

          * 通过反射操作其构造方法:

          * DBUtils: Person person = queryRunner.query(sql,newBeanHanlder(Person.class));

         public void demo2() throws Exception{

                   // 反射第一步获得Class对象.

                   Classclazz = Class.forName("com.itheima.demo3.reflect.Person");

                   // 采用默认的无参数的构造方法创建:

                   // Personperson = (Person) clazz.newInstance();

                   //person.run();

                   // 采用有参数的构造方法来创建对象:Person p = new Person("张森",38);

                   Constructorc =clazz.getConstructor(String.class,Integer.class);

                   Personp = (Person)c.newInstance("张森",38);

                   System.out.println(p.name);

                   System.out.println(p.age);

         }

          * 通过反射获得类中的属性:

         public void demo3() throws Exception{

                   // 反射第一步获得Class对象.

                   Classclazz = Class.forName("com.itheima.demo3.reflect.Person");

                   // 获得属性:

                   // 以下两种仅仅获得共有的属性:

                   //clazz.getField("name"); //获得某个属性

                   // Field[]fields = clazz.getFields(); //获得所有属性

                   // 获得私有的属性: p.name =对象.属性

                   Fieldfield1 =clazz.getDeclaredField("name");

                   field1.setAccessible(true);

                   Fieldfield2 =clazz.getDeclaredField("age");

                   field2.setAccessible(true);

                   Constructorc =clazz.getConstructor(String.class,Integer.class);

                   Personp = (Person)c.newInstance("张森",42);

                   Stringname = (String)field1.get(p);

                   Integerage = (Integer)field2.get(p);

                   System.out.println(name+"   "+age);

         }

 

* 通过反射获得类中的方法:并且让方法执行.

         public void demo4() throws Exception{

                   // 反射第一步获得Class对象.

                   Classclazz = Class.forName("com.itheima.demo3.reflect.Person");

                   // 获得类中的方法:

                   Methodmethod =clazz.getDeclaredMethod("run");

                   method.setAccessible(true);

                   method.invoke(clazz.newInstance());// p.run();

                  

                   // 获得带有参数的方法:

                   Methodmethod2 =clazz.getDeclaredMethod("sayHello", String.class);

                   Strings = (String)method2.invoke(clazz.newInstance(),"凤姐");// String s =p.sayHello("凤姐");

                   System.out.println(s);

         }

内省: 用来获得JavaBean的属性及属性的get或set方法.

内省的代码:

* 获得某个类中的属性:

        * * 一个JavaBean属性由getset方法确定的

       public void demo1() throws Exception{

              // 获得了Bean的信息

              BeanInfo beanInfo = Introspector.getBeanInfo(User.class);

              // 获得Bean的属性描述了   属性描述器

              PropertyDescriptor[] pds =beanInfo.getPropertyDescriptors();

              for(PropertyDescriptorpd:pds){

                     System.out.println(pd.getName());

                     /*pd.getReadMethod(); //获得这个属性对应的get方法

                     pd.getWriteMethod();//获得这个属性对应的set方法.

使用内省来写一个,对数据进行封装的工具类

public class MyBeanUtils {

       public static void populate(Object obj,Map<String,String[]> map) throws Exception{

              // 获得类的所有的属性的名称:

              BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());

              // 获得类中所有的属性:

              PropertyDescriptor[] pds =beanInfo.getPropertyDescriptors();

              for (PropertyDescriptorpd : pds) {

                     if(map.containsKey(pd.getName())){

                            Method method =pd.getWriteMethod();

                            // 执行set方法:

                            method.invoke(obj,map.get(pd.getName())[0]);

                     }

              }读到map中与属性名相同的key时,将value存入属性的值,完成封装

事务(数据安全)

  JDBC         (Spring事务更重要)                   

       特性/隔离级别

 

MYSQL的事务管理有两种方式:

(MYSQL数据库事务默认是自动提交的.Oracle数据库事务默认是不自动提交.)

 

* 1.手动开启事务

* start transaction; -- 开启事务

* 多条sql;

* commit/rollback;

 

* 2.设置一个自动提交参数

* show variables like '%commit%'; -- 查看与commit相关参数.

* set autocommit = 0; -- 将autocommit参数设置为OFF.

 

【JDBC中的事务管理】(掌握)

JDBC的事务的管理的API:




事务得加在业务层,因为事务必须由同一个Connection管理才能起作用.

即必须由一个Connection管理多个事务,将service层中的Connection传递给dao的多个方法.


* 事务管理的第一种办法发:向下传递Connection:

        *业务层转账的方法:

         public voidtransfer1(String from, Stringto, Double money) {

                   AccountDaoaccountDao =new AccountDao();

                   // 方式一:在业务层获得Connection,传递给DAO

                   Connectionconn =null;

                   try {

                            conn = JDBCUtils.getConnection();

                            conn.setAutoCommit(false);

                            //accountDao.outMoney(conn,from,money);

                            intd = 1/ 0;

                            //accountDao.inMoney(conn,to,money);

                            conn.commit();

                   }catch (Exceptione) {

                            try {

                                     conn.rollback();

                            }catch (SQLExceptione1) {

                                     e1.printStackTrace();

                            }

                            e.printStackTrace();

                   }

                  

public class JDBCUtils {

         private static finalComboPooledDataSourceDATA_SOURCE =newComboPooledDataSource();

          * 获得连接的方法

         public static ConnectiongetConnection(){

                   Connectionconn =null;

                   try {

                            conn =DATA_SOURCE.getConnection();

                   }catch (SQLExceptione) {

                            // TODOAuto-generated catch block

                            e.printStackTrace();

                   }

                   returnconn;

         }

        

         public static DataSourcegetDataSource(){

                   returnDATA_SOURCE;

         }

* 事务管理的第二种方法:绑定到当前线程

* 业务层转账的方法:

         public voidtransfer2(String from, Stringto, Double money) {

                   AccountDaoaccountDao =new AccountDao();

                   // 方式二:

                   try {

                            JDBCUtils2.beginTransaction();

创建连接并将连接与当前线程绑定 ,然后将事务设为手动提交

                            //accountDao.outMoney(from,money);

                            intd = 1/ 0;

                            //accountDao.inMoney(to,money);

                            JDBCUtils2.commitTransaction();

                   }catch (Exceptione) {

                            try {

                                     JDBCUtils2.rollBackTransaction();

                            }catch (SQLExceptione1) {

                                     e1.printStackTrace();

                            }

                            e.printStackTrace();

                   }

                  

public class JDBCUtils2 {

         private static finalComboPooledDataSourceDATA_SOURCE =newComboPooledDataSource();

         privatestaticfinalThreadLocal<Connection>tl = newThreadLocal<Connection>();

          * 获得连接的方法

         public static ConnectiongetConnection(){

                   Connectionconn =null;

                   try {

                            conn =tl.get();

                            if(conn ==null){

                                     conn = DATA_SOURCE.getConnection();

                                     tl.set(conn);

                            }

                   }catch (SQLExceptione) {

                            // TODOAuto-generated catch block

                            e.printStackTrace();

                   }

                   returnconn;

         }

         public static voidbeginTransaction() throws SQLException{

                   Connectionconn =null;

                   conn = tl.get();

                   if(conn ==null){

                            conn =DATA_SOURCE.getConnection();

                            tl.set(conn);

                   }

                   conn.setAutoCommit(false);

         }

         public static voidcommitTransaction()throws SQLException{

                   Connectionconn =tl.get();

                   conn.commit();

         }

         public static voidrollBackTransaction()throws SQLException{

                   Connectionconn =tl.get();

                   conn.rollback();

         }

         public static DataSourcegetDataSource(){

                   returnDATA_SOURCE;

         }

事务管理的第三种方法:使用DBUtils进行事务的管理:

         public void transfer(Stringfrom, Stringto, Double money) {

                   AccountDaoaccountDao =new AccountDao();

                   Connectionconn = JDBCUtils.getConnection();

                   try {

                            conn.setAutoCommit(false);

                            accountDao.outMoney(conn,from ,money);

                            intd = 1/ 0;

                            accountDao.inMoney(conn,to,money);

                            DbUtils.commitAndCloseQuietly(conn);

                   }catch (Exceptione) {

                            DbUtils.rollbackAndCloseQuietly(conn);

                            e.printStackTrace();

                   }

 

事务特性:

原子性:强调事务的不可分割.

一致性:强调的是事务的执行的前后,数据的完整性要保持一致.

隔离性:一个事务的执行不应该受到其他事务的干扰.

持久性:事务一旦结束(提交/回滚)数据就持久保持到了数据库.

如果不考虑事务的隔离性,引发一些安全性问题:

一边的操作影响到另外一边的操作,引发的问题.

读问题:

* 脏读          :一个事务读到另一个事务还没有提交的数据.

* 不可重复读      :一个事务到了另一个事务已经提交的update数据,导致在当前的事务中多次查询结果不一致.   (一个事务里面读到两个不同的结果)

* 虚读/幻读  :一个事务到另一个事务已经提交的insert数据,导致在当前的事务中多次的查询结果不一致. 

写问题:

* 引发两类丢失更新:

解决引发的读问题:

设置事务的隔离级别:

* read uncommitted            :脏读,不可重复读,虚读都可能发生.

* read committed         :可以避免脏读.  但是不可重复读和虚读有可能发生.

* repeatable read         :可以避免脏读,不可重复读.  但是虚读有可能发生.

* serializable                :可以避免脏读,不可重复读,虚读的发生.(安全性提升的同时性能降低)

MYSQL默认隔离级别:repeatable read ;   Oracle隔离级别:read committed

 

设置窗口的隔离级别为:read uncommitted:

set session  transaction isolation  level  read uncommitted;

 

查看窗口的隔离级别:select @@tx_isolation;

演示脏读的发生:

分别开启两个窗口:A,B

分别查看两个窗口的隔离级别:select @@tx_isolation;

设置A窗口的隔离级别为:read uncommitted:

* set sessiontransaction isolation level read uncommitted;

分别在两个窗口中开启事务:

* start transaction;

B窗口完成转账的操作:

* update account setmoney = money - 1000 where name = '张森';

* update account setmoney = money + 1000 where name = '凤姐';

A窗口查询数据:(钱已经到账---脏读)

* select * fromaccount;  -- A事务读到了B事务还没有提交的数据.

演示避免脏读,不可重复读发生

分别开启两个窗口:A,B

分别查看两个窗口的隔离级别:select @@tx_isolation;

设置A窗口的隔离级别为:read committed:

* set sessiontransaction isolation level read committed;

分别在两个窗口中开启事务:

* start transaction;

B窗口完成转账的操作:

* update account setmoney = money - 1000 where name = '张森';

* update account setmoney = money + 1000 where name = '凤姐';

A窗口中进行查询:

* select * fromaccount; -- 避免脏读.

B窗口提交事务:

* commit;

A窗口中再次查询:

* select * fromaccount; -- 转账成功.(不可重复读:一个事务读到另一个事务中已经提交的update的数据,导致多次查询结果不一致.)

演示避免不可重复读:

分别开启两个窗口:A,B

分别查看两个窗口的隔离级别:select @@tx_isolation;

设置A窗口的隔离级别为:repeatable read:

* set sessiontransaction isolation level repeatable read;

分别在两个窗口中开启事务:

* start transaction;

B窗口完成转账的操作:

* update account set money = money - 1000where name = '张森';

* update account set money = money + 1000where name = '凤姐';

在A窗口查询:

* select * from account; -- 转账没有成功:避免脏读.

在B窗口提交事务:

* commit;

在A窗口中再次查询:

* select * from account; -- 转账没有成功:避免不可重复读.

 

演示避免虚读的发生:

分别开启两个窗口:A,B

分别查看两个窗口的隔离级别:select @@tx_isolation;

设置A窗口的隔离级别为:serializable:

* set sessiontransaction isolation level serializable;

A,B两个窗口中分别开启事务:

* start transaction;

B窗口中完成一个insert操作:

* insert into account values (null,'王老师',10000);

在A创建中进行查询的操作:

* select * from account; -- 没有查询到任何结果.

在B窗口提交事务:

* commit; -- A窗口马上就会显示数据.

 

 

MVC和EE的三层结构:
































原创粉丝点击