API(Application Programming Interface),今天我主要谈的是,重量级组件SqlSessionFactory创建过程中的涉及到的几个类的API.首先我们来说一下,线程出现安全问题的条件:
(1)只有单例模式才会出现线程安全的问题
(2)单例对象中含有可修改的属性.
(3)多线程环境(即多个线程共享同一个单例对象)
单例模式的使用场景:
(1)当对象需要频繁的创建和销毁时
(2)对象的创建和销毁过程需要消耗大量的系统资源(CPU和运行内存)
(3)需要将该对象声明成全局的变量
(1)SqlSession接口,该接口的实现类是org.apache.ibatis.sessiondefaults.DefaultSqlSession,该对象时多例的,(一个线程会一个,一个线程简单来说就代表的是一个用户)
(2)SqlSessionFactory接口,该接口的实现类是org.apache.ibatis.sessiondefaults.DefaultSqlSessionFactory,这个对象的作用就是为了创建SQLSession对象,然而SQLSession对象时多例的,每次为一个线程创建一个SQLSession对象,都需要一个SQLSessionFactory对象,但是SQLSessionFactory又是一个重量级组件,并且DefaultSqlSessionFactory类中没有含有可以修改的成员变量,所以SQLSessionFactory可以使用单例模式,它的生命周期和整个应用程序的生命周期一致.代码如下:
//声明底层对象
private static SqlSessionFactory factory;
public static SqlSession getSqlSession() throws IOException {
//获取指定文件的输入流对象
InputStream is = Resources.getResourceAsStream("mybatis.xml");
if(factory == null) {
factory = new SqlSessionFactoryBuilder().build(is);
}
return factory.openSession();
}
(3)SQLSessionFactoryBuilder类,该类的作用就是为了创建SQLSessionFactory对象,由于SQLSessionFactory是单例对象,只需要创建一次,一旦创建完毕,即可被销毁,所以SQLSessionFactory定义为局部变量.
==========================
下面是对一些疑问的总结:
(1)我们在加载主配置文件的IO流为啥没有进行关闭,却没有警告?
InputStream is = Resources.getResourceAsStream("mybatis.xml");因为你,源码中已经对这个IO流进行了关闭,所以不需要我们关闭也同样能够使用,并且没有警告.
(2)factory.openSession()和factory.openSession(false)两个重载方法,虽然参数不同,但是二者的作用是一样的,此时的false 的作用是关闭自动提交的功能,所以如果我们使用Session执行除了select之外的操作命令时,如果我们不手动提交,是不会写入到数据库的,千万注意.
(3)dirty,脏的意思,是一个Boolean类型的值,它的功能是判断内存中的数据和数据库中的数据是否一致,一致值为false,不一致为true,默认的是false.
(4)阅读源码可以发现,增删改查的底层操作都是归于update,只不过是调用相应的参数一致的update方法.
(5)sqlsession 的提交最终会转换成事务的提交.
============================================================================ CURD(create,update,retrive,delete)
CURD,也可以称为CRUD指的是对数据库的增删改查的操作,
@Test
public void test02() {
Student student = new Student("李四",20,98.5);
//插入前:Student [id=null, name=李四, age=20, score=98.5]
System.out.println("插入前:"+student);
dao.insertStudentCatchId(student);
System.out.println("插入后:"+student);
//插入后:Student [id=null, name=李四, age=20, score=98.5]
//分析为什么二者输出的结果数一致的:第一个student的ID为null,这个是肯定的,关键是第二个学生的
//student的虽然在数据库中生了id,并且插入到了数据库中,但是并没有把数据库中的id值赋值给在内存中
//的student的id,所以输出的结果是一致的.
}
为什么数据在插入之后,id的值依旧是空值呢?
数据在插入到数据库之后,数据库会自动的生成相应的ID值,但是数据库没有能力把生成的ID值,赋值给内存中的对象的id属性.
那么我们如何获取新插入的数据的ID值呢?
(1)select @@identity该标签的作用是显示系统变量的值,其中identity表示的就是新插入的数据的ID
(2)select last_insert_id();这个函数的功能也是获取新插入的数据的ID.如果你认为这样写有点丑,我们还可以起别名,例如:select last_insert_id() newID;此时的newId就是别名,
这两种方式在使用的时候,必须要紧跟着insert语句,否则执行的结果是0,并且我们这哎Mapper.xml文件中使用的时候,应该是这样写的:
<!-- 获取新插入数据的id值 -->
<insert id="insertStudentCatchId" parameterType="Student">
insert into student (name,age,score) values(#{name},#{age},#{score})
<selectKey resultType="int" keyProperty="id">
<!-- select @@identity -->
select last_insert_id()
</selectKey>
</insert>
注意:<selectKey resultType="查询结果的数据类型" keyProperty="表示的是查询的结果要封装给所传参数的哪一个属性"
order="Before/After"/>order的作用就是,说明insert语句的执行顺序和ID的生成顺序谁先谁后,mysql是限制性插入,然后生成id,oracle是先生成id,在插入,然后执行insert.
注意:在select标签中,我们必须制定查询的结果应该被封装成的类型,使用resultType或者是resultMap来限制,
当我们使用模糊查询是,常用的有三种方法,我们谈谈他们的优缺点
(1)select * from student where name like '%' #{} '%'(推荐使用)
(2)select * from student where name like concat('%' ,#{}, '%')第一种和第二种使用的都是PreparedStatment,预编译处理SQL语句,执行效率比较高.
(3)select * from student where name like '%${}%'这种方法使用的是纯粹的字符串拼接,无法防止Sql注入.有可能数据泄露.