MyBatis注解开发
注解方式比较简单,但是实际开发不推荐使用注解,使用配置文件的方式,不需要改源代码。
@Insert:添加
@Update:修改
@Delete:删除
@Select:查询
@Result:实现结果集封装
@Results:可以和@Result一起使用,封装多个结果集
@One:实现一对一和多对一的结果集封装
@Many:实现一对多结果级封装片
一、使用注解完成CRUD
1. SqlMapConfig.xml配置文件
<mappers>
<!--第一种方式:class引入接口,只能引入一个接口-->
<mapper class="com.qcby.dao.UserAnnoDao"/>
<!--第二种方式:针对com.qcby.dao包下的所有的接口-->
<package name="com.qcby.dao"/>
</mappers>
2. UserDao接口方法和注解的编写
import com.qcby.entity.User;
import org.apache.ibatis.annotations.*;
import javax.jws.soap.SOAPBinding;
import java.util.List;
public interface UserDao {
//查询所有
@Select("select * from user")
@Results(id="userMap",value = {
@Result(property = "id",column = "id"),
@Result(property = "username",column = "username"),
@Result(property = "birthday",column = "birthday"),
@Result(property = "sex",column = "sex"),
@Result(property = "address",column = "address")
})
public List<User> findAll();
//通过ID查询
@Select("select * from user where id = #{id}")
@ResultMap(value = "userMap")
public User findById(int id);
//增加
@Insert("insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
@SelectKey(statement="select last_insert_id()",keyColumn = "id",keyProperty = "id",before =false,resultType =Integer.class)
public int insert(User user);
//更新
@Update("update user set username = #{username},birthday = #{birthday},sex = #{sex},address = #{address} where id = #{id}")
public int update(User user);
//删除
@Delete("delete from user where id = #{id}")
public int delete(int id);
//查询数量
@Select("select count(*) from user")
public int findCount();
//模糊查询
@Select("select * from user where username like #{username}")
public List<User> findByName(String username);
}
3. UserTest测试方法的编写
public class UserTest {
private InputStream in = null;
private SqlSession session = null;
private UserDao mapper = null;
@Before //前置通知, 在方法执行之前执行
public void init() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession();
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
}
@After //@After: 后置通知, 在方法执行之后执行 。
public void destory() throws IOException {
//释放资源
session.close();
in.close();
}
/**
* 测试查询所有的方法
*/
@Test
public void findAll() throws IOException {
List<User> users = mapper.findAll();
for (User user:users) {
System.out.println(user.toString());
}
}
@Test
public void findById(){
User user = mapper.findById(4);
System.out.println(user.toString());
}
@Test
public void insert(){
User user = new User();
user.setSex("女");
user.setUsername("小美");
user.setBirthday(new Date());
user.setAddress("保定");
int insert = mapper.insert(user);
session.commit();
System.out.println(insert);
}
@Test
public void update(){
User user = new User();
user.setId(22);
user.setSex("女");
user.setUsername("小美");
user.setBirthday(new Date());
user.setAddress("上海");
int insert = mapper.update(user);
session.commit();
System.out.println(insert);
}
@Test
public void delete(){
int delete = mapper.delete(22);
session.commit();
System.out.println(delete);
}
@Test
public void findCount(){
int count = mapper.findCount();
session.commit();
System.out.println(count);
}
@Test
public void findByName(){
List<User> list = mapper.findByName("%a%");
for (User user : list) {
System.out.println(user);
}
session.close();
}
}
二、多对一的注解查询
1.多对一立即加载查询
①.StudentDao接口的方法编写
@Select(" SELECT student.*,teacher.Tname FROM student LEFT JOIN teacher on student.t_id = teacher.id")
@Results(value = {
@Result(property = "id",column = "id"),
@Result(property = "Sname",column = "Sname"),
@Result(property = "sex",column = "sex"),
@Result(property = "age",column = "age"),
@Result(property = "t_id",column = "t_id"),
@Result(property = "teacher.Tname",column = "Tname")
})
public List<Student> getStudent();
②.进行测试
@Test
public void getStudent(){
List<Student> student = mapper.getStudent();
for (Student student1:student) {
System.out.println(student1.toString());
}
}
2.多对一延迟加载查询
①.StudentDao接口的方法编写
@Select("select * from student")
@Results(value = {
@Result(property = "id",column = "id"),
@Result(property = "Sname",column = "Sname"),
@Result(property = "sex",column = "sex"),
@Result(property = "age",column = "age"),
@Result(property = "teacher",column = "t_id",one=@One(select = "com.qcby.dao.TeacherDao.getTeacher",fetchType = FetchType.LAZY))
})
public List<Student> getStudent();
②.TeacherDao接口的方法编写
@Select("select * from teacher where id = #{t_id}")
Teacher getTeacher(Integer id);
③.进行测试
@Test
public void getStudent(){
List<Student> student = mapper.getStudent();
for (Student student1:student) {
System.out.println(student1.toString());
}
}
三、一对多的注解查询
一对多查询,使用延迟加载的方式查询
①.TeacherDao接口的方法编写
//查询所有延迟加载
@Select("select * from Teacher")
@Results(value = {
@Result(property = "id",column = "id"),
@Result(property = "Tname",column = "Tname"),
@Result(property = "students",column = "id",many =@Many(select = "com.qcby.dao.StudentDao.findByUid",fetchType = FetchType.LAZY))
})
public List<Teacher> findAllLazy();
②.StudentDao接口的方法编写
@Select("select * from student where t_id = #{t_id}")
public Student findByUid(int uid);
③.进行测试
@Test
public void findAllLazy(){
List<Teacher> list = mapper.findAllLazy();
for (Teacher teacher: list) {
System.out.println(teacher.toString());
}
}
Mybatis事务
1.事务是什么?
多个操作同时进行,那么同时成功,那么同时失败。这就是事务。
事务有四个特性:一致性、持久性、原子性、隔离性
比如有一个订单业务
1.订单表当中添加一条记录
2.商品数量数据更新(减少)
3.....
当多个任务同时进行操作的时候,这些任务只能同时成功,或者同时失败。
2.Mybatis关于事务的管理
MyBatis框架中的事务默认是手动提交的,也就是每次编写程序都需要调用commit()方法提交事务
<transactionManager type="JDBC"></transactionManager> ====》程序员自己控制处理的提交和回滚
设置自动提交事务,可以通过openSession的方法进行设置,openSession(true) 自动提交事务。代码如下:(配置中的策略不变)
public class UserTest {
private InputStream in = null;
private SqlSession session = null;
private UserDao mapper = null;
@Before //前置通知, 在方法执行之前执行
public void init() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession(true); //关闭
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
}
@After //@After: 后置通知, 在方法执行之后执行 。
public void destory() throws IOException {
//释放资源
session.close();
in.close();
}
@Test
public void insertMoreByList(){
User user1 = new User("a1","男","北京");
User user2 = new User("a2","男","北京");
User user3 = new User("a3","男","北京");
List<User> users = Arrays.asList(user1,user2,user3);
int result = mapper.insertMoreByList(users);
//session.commit(); //手动提交事务
System.out.println(result);
}
}
缓存
一、什么是缓存
1.什么是缓存
- 存储在内存当中的数据
- 将用户常使用的数据存放在缓存(内存)当中,用户查询数据就不用从磁盘(关系型数据库文件)当中查询,从缓存当中查询,从而提高查询效率,解决了高并发系统的性能问题
2.为什么使用缓存
减少和数据库的交互次数,减少系统开销,提高系统效率
3.什么样的数据能使用缓存
经常查询并且不常改变的数据
二、Mybatis缓存
- mybatis包含了一个非常强大的查询缓存特性,他可以非常方便的定制和配置缓存。缓存可以极大的提高查询的效率。
- mybatis系统当中默认定义了两级缓存:一级缓存和二级缓存。
默认情况之下,只有一级缓存开启(sqlSession级别的缓存)
二级缓存需要手动开启配置,需要局域namespace级别的缓存。
1.一级缓存
一级缓存也叫本地缓存
与数据库同一次会话期间查询到的数据会放入的本地缓存当中。
如果以后需要获取相同的数据直接去缓存当中拿,没必要再去查询数据库
测试一级缓存
public class UserTest {
private InputStream in = null;
private SqlSession session = null;
private UserDao mapper = null;
/**
* 测试查询所有的方法
*/
@Test
public void findById() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession();
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
User user1 = mapper.findById(1);
System.out.println(user1.toString());
System.out.println("-----------------");
User user2 = mapper.findById(1);
System.out.println(user2.toString());
System.out.println(user1 == user2);
//释放资源
session.close();
in.close();
}
}
缓存失效的情况
sqlSession不同
sqlSession相同,查询条件不同
sqlSession相同,两次查询之间执行了增删改操作!
sqlSession相同,手动清除一级缓存
①:sqlSession不同
2.sqlSession相同,查询条件不同, 多次查询不同的情况,不会导致缓存失效
3.sqlSession相同,两次查询之间执行了增删改操作!
4.sqlSession相同,手动清除一级缓存
2.二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactroy创建SqlSession查询结果会被缓存;此后若再次执行相同的查询语句,结果会从一个缓存中获取。
(1).二级缓存开启的条件
①:在核心配置文件中,设置全局属性caheEnable=“true”。
②:在映射件中置
③:查询数据所转换的实体类类型必须实现序列化接口
④:二级缓存必须在SqlSession关闭或提交之后有效
(2).二级缓存失效的情况
两次查询之间行了任意的增删改,会使得一级二级缓存同时失效
(3).开启二级缓存
①.在SqlMapConfig.xml配置文件中开启二级缓存
<!‐‐ 开启二级缓存 ‐‐>
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
②. 在UserDao.xml配置文件声明使用二级缓存
<!--使用二级缓存-->
<cache/>
③:查询数据所转换的实体类类型必须实现序列化接口
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// get set方法 .....
}
④:二级缓存必须在SqlSession关闭或提交之后有效
@Test
public void findById() throws IOException {
// 1.加载SqlMapConfig配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建sqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//3.sqlSessionFactory创建sqlSession
SqlSession sqlSession = factory.openSession();
SqlSession sqlSession2 = factory.openSession();
//4.通过Session创建UserDao接口代理对象
UserDao mapper = sqlSession.getMapper(UserDao.class);
User user1 = mapper.findById(1);
System.out.println(user1.toString());
// 将其一级缓存的数据放进二级缓存中,并清空一级缓存
sqlSession.close();
System.out.println("-----------------");
UserDao mapper2 = sqlSession2.getMapper(UserDao.class);
User user2 = mapper2.findById(1);
System.out.println(user2.toString());
// 将其一级缓存的数据放进二级缓存中,并清空一级缓存
sqlSession2.close();
System.out.println(user1 == user2);
resourceAsStream.close();
}
测试结果
(4).重要结论
打印发现2个对象的地址值不一样,但是确实只发送了一次SQL语句的查询,二级缓存中存储的是数据,不是对象。
(5).Catch参数的具体细节
eviction(收回策略)
LRU(最近最少使用的):移除最长时间不被使用的对象,这是默认值。
FIFO(先进先出):按对象进入缓存的顺序来移除它们。
SOFT(软引用):移除基于垃圾回收器状态和软引用规则的对象。
WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushinterval(刷新间隔) 可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。
默认情况不设置,即没有刷新间隔,缓存仅仅在调用语句时刷新。
size(引用数目)
可以被设置为任意正整数,要记住缓存的对象数目和运行环境的可用内存资源数目。默认值是1024 。
readOnly(只读) 属性可以被设置为 true / false。
true:只读缓存:会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改, 这提供了很重要的性能优势。
false读写缓存: 通过序列化返回缓存对象的拷贝,这种方式会慢一些,但是安全,因此默认是 false。
配置结果
<cache eviction="FIFO"
flushInterval="6000"
size="512"
readOnly="true"/>
三、Mybatis缓存查询顺序
先查询二级缓存,因为二级缓存中可能会有其他程序查询出来的数据,可以直接拿来使用
如果二级缓存命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存的数据会写入二级缓存