Mybatis(黑马)
1.框架概述
课程介绍
三层架构和ssm框架的对应关系
jdbc操作数据库的问题分析
- jdbc代码回顾
java
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库链接
connection = DriverManager
.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","ro
ot", "root");
//定义 sql 语句 ?表示占位符
String sql = "select * from user where username = ?";
传智播客——专注于 Java、.Net 和 Php、网页平面设计工程师的培训
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
//获取预处理 statement
preparedStatement = connection.prepareStatement(sql);
//设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的
参数值
preparedStatement.setString(1, "王五");
//向数据库发出 sql 执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//遍历查询结果集
while(resultSet.next()){
System.out.println(resultSet.getString("id")+"
"+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//释放资源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
上边使用 jdbc 的原始方法(未经封装)实现了查询数据库表记录的操作
在传统的jdbc中,我们为啥实现查询这个功能,需要配置获取连接,获取预处理对象,关闭资源这些操作。但是这些操作是重复的。我们程序员应该将个更多的精力放在解决需求(sql语句(使用sql语句实现需求))上面去,而不是放在这些非业务的操作上去。
mybatis概述
环境搭配--前期准备
- 数据库和实验用表的准备
CREATE DATABASE mybatis1;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT '用户名称',
`birthday` DATETIME DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) DEFAULT NULL COMMENT '性别',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`username`,`birthday`,`sex`,`address`)
VALUES (41,'老王','2018-02-27 17:47:08','男','北京'),
(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),
(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),
(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),
(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');
SELECT * FROM USER
-
环境搭建的步骤
-
可能需要用到的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>day1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging><!--打包方法-->
<!--依赖-->
<dependencies>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.14</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
- domain
package com.itheima.domain;
import java.io.Serializable;
import java.util.Date;
/*user表对应的实体类*/
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public User() {
}
public User(Integer id, String username, Date birthday, String sex, String address) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
/**
* 获取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 设置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 获取
* @return username
*/
public String getUsername() {
return username;
}
/**
* 设置
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 获取
* @return birthday
*/
public Date getBirthday() {
return birthday;
}
/**
* 设置
* @param birthday
*/
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
/**
* 获取
* @return sex
*/
public String getSex() {
return sex;
}
/**
* 设置
* @param sex
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* 获取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 设置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return "User{id = " + id + ", username = " + username + ", birthday = " + birthday + ", sex = " + sex + ", address = " + address + "}";
}
}
- mybatis主配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis主配置文件-->
<configuration>
<!--配置环境-->
<environments default="mysql">
<environment id="mysql">
<!--配置事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池(数据源)-->
<dataSource type="POOLED">
<!--配置连接池的信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1"/>
<property name="username" value="root"/>
<property name="password" value="888888"/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>
</configuration>
- userDao映射配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IUserDao">
<!--配置查询所有-->
<select id="findAll">
select * from user
</select>
</mapper>
主配置文件和映射配置文件的约束在资料里面有
环境搭配的注意事项
mybatis的入门
package com.itheima.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import javax.annotation.Resource;
import java.io.InputStream;
import java.util.List;
public class MyBatisTest {
public static void main(String[] args) throws Exception{
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产sqlSession对象
SqlSession sqlSession = factory.openSession();
//4.使用sqlSession创建dao接口的代理对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
//5.使用代理对象执行方法
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6.释放资源
sqlSession.close();
}
}
- 在mybatis
3.4.5
中需要加上resultType,但是在mybatis 3.5.4就不用加resultType也可以识别出来
- 将会出现下面的异常
入门案例中的设计模式分析
mybatis注解开发和编写dao实现类的方法
- 使用注解的方式实现前面的入门案例
写dao的实现类也可以完成相同的功能,但是让代码过于繁杂,程序员无法将精力集中在业务中
3.自定义mybatis
执行查询的所有分析
- 前提要求实体类属性和表的列名一致
关注点1.如何创建代理对象以及使用的设计模式带来的优势2.这个过程中调用的组合关系
不必关注点:1.jdbc的执行2.xml的解析不需要去深究
为什么将映射文件中的全类名和SQL语句封装成一个对象而不是参数?
如果封装成参数,则有多条sql语句的时候不知道谁和谁是对应的(全类名和sql语句是一一对应的)
mybatis自定义编写---根据测试类中缺少的创建类和接口
- 根据前面的分析进行编写
首先我们pom.xml中将mybatis删除掉
mybatis自定义编写---解析xml的工具类的介绍
- 因为xml解析的原理不是很重要,我们使用工具类来完成
我们直接从资料中导入XMLconfigBuilder用于XML解析
此时已经基本理解XML对配置文件的解析操作。了解了映射关系
个人理解: - 1.读取主配置文件
我们将读取主配置文件的信息以流传递给xml解析器:1.先将主配置文件中连接数据库所需要的属性解析出来,并将其的值设置给Configuration
配置类中的属性2.根据映射文件的位置找到映射文件(然后开始解析映射文件)3.得到映射文件中所有的mapper标签(一个mapper代表这个dao的一个方法)4.获取映射文件的属性(映射文件的路径)传递给loadMapperConfiguration())
函数将映射关系封装成一个MAp集合5 将返回的map对象追加到Configuration配置类的Map
集合(专门记录映射关系)中. loadMapperConfiguration(String mapperPath)
方法--专门封装映射关系(解析映射文件)
1.方法中传入了映射文件的路径2.创建map集合Map<String,Mapper> mappers
3.in = Resources.getResourceAsStream(mapperPath)
获取文件中的数据到流中4.将流中的数据传入解析器开始解析5.获取namespace(dao全限定列名)作为key的一部分6.获取所有的select标签7.循环遍历select标签,获取id值(方法名)和namespace一起作为key名。得到resultType
值和select语句,他们被一起封装成Mapper
对象,并将它们作为value值8.将key和value一起封装成mapper对象,并返回
自定义mybatis编码--创建2个默认实现类并分析类之间的操作
实现基于xml查询所有的操作
对mybatis执行过程的概述
本质上只是干了2件事:1.解析XML文件2.创建代理对象
我们将XML文件的信息传入解析器后,解析器将进行解析,得到连接信息(driver,username,password,url)和映射信息(被代理类全类名+方法名和增强的方法)(select语句+返回类型))并将他们封装在Configuration
类中
1.根据连接信息将会注册驱动并获取连接对象,并创建预处理对象2.session.getMapper(IUserDao.class);
将会创建代理对象3.当调用相应的方法时,将通过映射关系查找出要执行的增强后的方法并执行
对mybatis中代理模式的理解
- 1.mybatis代理模式来执行sql语句,即在动态生成的代理方法中只是执行SQL,而没有原始方法(没有原始的被代理类)
- 2.我们的映射关系,可以理解为AOP中的切入点方法和通知方法相互绑定。和代理类中增强部分方法调用原始方法一样,所以我们的映射关系可以抽象成代理类
(如果我知道动态生成的代理类的结构,也会是这样的结构) - 3.既然我们生成的代理类就是一个映射,所以我们也可以说我们的dao是一个映射mapper
Mybatis的CRUD
- 学习内容
回顾自定义mybatis的流程分析
基于注解的自定义再分析
回顾环境搭配---实现查询所有功能
- 配置文件的约束都是直接引入的
- dao
package com.athuima.dao;
import com.athuima.domain.User;
import java.util.List;
public interface UserDao {
List<User>findAll();
}
- 主配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis是主配置文件-->
<configuration>
<environments default="mysql"><!--环境名称-->
<environment id="mysql">
<!--事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池-->
<dataSource type="POOLED">
<!--配置连接的信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="888888"/>
</dataSource>
</environment>
</environments>
<!--配置映射文件的位置-->
<mappers>
<mapper resource="com/athuima/dao/userDao.xml"></mapper>
</mappers>
</configuration>
- 映射配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.athuima.dao.UserDao">
<select id="findAll" resultType="com.athuima.domain.User">
select * from user;
</select>
</mapper>
- 测试类
@Test
public void test() throws Exception{
//1.读取主配置文件数据
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.创建工厂
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
//3.利用工厂生成数据库操作对象sqlSession
final SqlSession sqlSession = sqlSessionFactory.openSession();
//4.使用sqlSession生成代理对象
final UserDao userDao = sqlSession.getMapper(UserDao.class);
//5.使用代理对象执行方法
final List<User> list = userDao.findAll();
//6.遍历结果集
for (User user : list) {
System.out.println(user);
}
}
注意:当我们切换成使用注解时,一定要将原来目录下的映射文件清除或者是换一个位置
保存操作
我们按照xml配置来实现
- userDao.xml中
<mapper namespace="com.athuima.dao.UserDao">
<select id="findAll" resultType="com.athuima.domain.User">
select * from user;
</select>
<insert id="save" parameterType="com.athuima.domain.User">/*传入sql语句参数的类型*/
insert into user (username,sex,birthday,address)values (#{username},#{sex},#{birthday},#{address});
</insert>
</mapper>
- 主配置文件
<!--配置映射文件的位置-->
<mappers>
<!--使用xml配置-->
<mapper resource="com/athuima/dao/userDao.xml"></mapper>
<!--使用注解-->
<!-- <mapper class="com.athuima.dao.UserDao"></mapper>-->
</mappers>
- dao
public interface UserDao {
/* @Select("select * from user")*/
List<User>findAll();
int save(User user);//保存方法
}
- 测试方法
package com.test;
import com.alibaba.druid.pool.DruidDataSource;
import com.athuima.dao.UserDao;
import com.athuima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;
public class MybatisTest {
private InputStream in;
private SqlSession sqlSession;
private UserDao userDao;
//抽取关闭资源的方法
@After//在测试方法之后执行
public void close() throws Exception {
//提交事务
sqlSession.commit();
sqlSession.close();
in.close();
}
@Before//在测试方法之前执行
//抽取初始化方法
public void init() throws Exception{
//1.读取主配置文件数据
in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.创建工厂
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
//3.利用工厂生成数据库操作对象sqlSession
sqlSession = sqlSessionFactory.openSession();
//4.使用sqlSession生成代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@Test
public void testSelect() throws Exception{
//5.使用代理对象执行方法
final List<User> list = userDao.findAll();
//6.遍历结果集
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testSave() throws Exception{
User user = new User();
user.setUsername("石文涛");
user.setSex("男");
user.setBirthday(new Date());
user.setAddress("北京");
//5.使用代理对象执行方法
final int row = userDao.save(user);
System.out.println(row);
}
}
当我们执行sql后,没有报错,但是数据的插入没有成功
我们需要在操作的最后进行手动事务提交
修改和删除操作
- 映射文件
<!--更新用户-->
<update id="updateUser" parameterType="com.athuima.domain.User">
update user set username=#{username},address=#{address},sex=#{sex} ,birthday=#{birthday} where id=#{id};
</update>
<!--删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id = #{dswwwxsssss}
</delete>
- dao
int updateUser(User user);
int deleteUser(Integer id);
- 测试
@Test
public void testUpdateUser() throws Exception{
User user = new User();
user.setId(53);
user.setSex("女");
user.setUsername("火云邪神");
user.setAddress("汤成一片");
userDao.updateUser(user);
}
@Test
public void testDeleteUser() throws Exception{
userDao.deleteUser(51);
}
其他的操作和前面的一样
查询一个和模糊查询
- 映射文件
- dao
//查询一个用户
User findById(Integer id);
/*模糊查询*/
List<User> findByUsername(String username);
- 测试
@Test
public void testSelectOne() throws Exception{
final User user = userDao.findById(51);
System.out.println(user);
}
@Test
public void testFindByUsername() throws Exception{
final List<User> users = userDao.findByUsername("%王%");
for (User user : users) {
System.out.println(user);
}
}
查询返回单值操作
- 映射文件
<!--查询总数据的条数-->
<select id="findTotalCount" parameterType="int">
select count(*) from user;
</select>
-
dao
int findTotalCount();
-
测试
@Test
public void testFindTotalCount() throws Exception{
final int count = userDao.findTotalCount();
System.out.println(count);
}
2种模糊查询写法细节分析
保存操作的细节---获取保存数据的id
** SELECT LAST_INSERT_ID();这个sqk语句可以查询出最后保存的id号,需要在你插入操作之后历即调用 **
- 映射文件
<!--插入用户-->
<insert id="save" parameterType="com.athuima.domain.User">/*传入sql语句参数的类型*/
<!--配置插入操作之后,获取插入数据的Id-->
<!--order:表明在最后一次插入操作之后执行-->
<!--keyColumn:对应的列,keyProperty:对应的属性-->
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user (username,sex,birthday,address)values (#{username},#{sex},#{birthday},#{address});
</insert>
- 测试
@Test
public void testSave() throws Exception{
User user = new User();
user.setUsername("张三");
user.setSex("男");
user.setBirthday(new Date());
user.setAddress("日本");
System.out.println("插入之前:"+user);//打印出保存之前的user
//5.使用代理对象执行方法
final int row = userDao.save(user);
System.out.println("插入之后:"+user);//打印出保存之后的user
}
mybatis中的参数深入---使用实体类的包装对象作为查询条件
- 前面查询条件的回顾
当我的查询条件有多个对象时,我们可以将多个对象包装成一个domain对象作为查询条件
- 映射文件
<!--使用实体类的包装类作为查询条件-->
<select id="findByQueryVo" parameterType="com.athuima.domain.QueryVo">
select * from user where username like #{user.username}
</select>
- 查询条件包装类--domain
package com.athuima.domain;
//查询条件的包装类
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
- dao接口
/根据 QueryVo 中的条件模糊查询用户
List<User> findByQueryVo(QueryVo vo);
- 测试
@Test
public void testFindByQueryVo() throws Exception{
QueryVo vo= new QueryVo();
User user = new User();
user.setUsername("王");
vo.setUser(user);
final List<User> users = userDao.findByUsername("王");
for (User u : users) {
System.out.println(u);
}
}
mybatis中返回值的深入----调整实体类的属性解决增和改方法的报错
我们前面在查询的时候要求我们的实体类和数据库的列名一致,但是如果我们不一致会怎样呢
- 对于非查询操作来说
此时对于非查询的报错是可预料到的,我们将映射文件中的属性值改成新的属性值,就可以运行了 - 但是在查询中呢
<!--查询所有-->
<select id="findAll" resultType="com.athuima.domain.User">
select * from user;
</select>
@Test
public void testSelect() throws Exception{
//5.使用代理对象执行方法
final List<User> list = userDao.findAll();
//6.遍历结果集
for (User user : list) {
System.out.println(user);
}
}
- 原因分析
我们的属性用户名只是改变了大小写其他的并没有改变,而列名在window系统下不区分大小写所有username可以封装成功
mybatis中返回值深入----解决实体类属性和数据库列名不对应的2种方式
我们封装不成功的原因就是属性名和列名匹配不上,我们需要解决的就是让他们匹配上就可以了
- 1.第一种方法---起别名
我可以可以在执行sql语句的时候起别名,让查询出来的列名和属性名一致 - 查询所有的映射文件
<!--查询所有-->
<select id="findAll" resultType="com.athuima.domain.User">
select id as userId,username as userName,birthday as userBirthday,sex as userSex,address as useraddRess from user;
</select>
- 查询结果
发现此时可以封装成功了。这是从sql语句的层面上解决问题,执行的效率是最高的
-
2.方式2:在映射文件中配置实体类属性和数据库列名的对应关系
-
映射文件中的配置
<!--配置查询结果的列名和实体类中的属性名的映射关系-->
<resultMap id="usermap" type="com.athuima.domain.User">
<!--主键字段的映射-->
<id property="userId" column="id"></id>
<!--非主键字段的对应-->
<result property="userName" column="username"></result>
<result property="userBirthDay" column="birthday"></result>
<result property="userSex" column="sex"></result>
<result property="useraddRess" column="address"></result>
</resultMap>
<!--查询所有-->
<select id="findAll" resultMap="usermap">
<!-- select id as userId,username as userName,birthday as userBirthday,sex as userSex,address as useraddRess from user;-->
select * from user;
</select>
注意此时我们需要将返回的结果resultType改成resultMap表示使用结果映射
- 这2种方式的说明
直接在sql语句中修改添加别名,这种效率是最高的,但是这种方法的开发效率比较低,每条查询语句的都需要添加别名。方法2,我们添加结果映射,这将带来多解析一段xml文件,执行效率比较低,但是开发效率比较高
Mybatis 实现 DAO 的传统开发方式(手动写dao的实现类)
mybatis编写dao的实现类---查询列表
- dao接口
package com.atheima.dao;
import com.atheima.domain.User;
import java.util.List;
public interface UserDao {
/*查询所有*/
List<User> findAll();
}
- dao实现类
package com.atheima.dao.impl;
import com.atheima.dao.UserDao;
import com.atheima.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.List;
//UserDao的实现类
public class UserDaoImpl implements UserDao {
//定义工厂用于创建sqlSession对象
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public List<User> findAll() {
//工厂创建sqlSession对象
final SqlSession sqlSession = sqlSessionFactory.openSession();
final List<User> users = sqlSession.selectList("com.atheima.dao.UserDao.findAll");//参数是映射的key
sqlSession.close();
return users;
}
}
- 映射文件
<mapper namespace="com.atheima.dao.UserDao">
<select id="findAll" resultType="com.atheima.domain.User">
select * from user;
</select>
著配置文件是一样的
- 测试
public class MyBatisTest {
private InputStream in;
private SqlSessionFactoryBuilder builder;
@After
public void close() throws Exception{
in.close();
}
@Before
public void init() throws Exception{
//加载配置文件
in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.获取SqlSessionFactory
builder = new SqlSessionFactoryBuilder();
}
@Test
public void testFindAll() throws Exception{
//3.创建dao
UserDao userDao = new UserDaoImpl(builder.build(in));
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
}
从这个例子我们可以看出,如果我们使用代理模式动态创建出代理(dao的实现类),这样可以屏蔽很多细节。比如说完全屏蔽了使用sqlSession对象调用selectList方法,这些都是在动态代理中自动完成的。我们从实现类的结构可以看出我们的一个映射(mapper
)其实就是一个实现类,所有说我们的动态代理完全可以依据映射关系被动态的创建出来
mybatis编写dao的实现类---保存操作
- 映射文件
<insert id="saveUser" parameterType="com.atheima.domain.User" >
insert into user (id,username,birthday,sex,address)values (#{id},#{username},#{birthday},
#{sex},#{address});
</insert>
- dao实现类
@Override
public int saveUser(User user) {
//1.创建session对象
final SqlSession sqlSession = sqlSessionFactory.openSession();
//2.执行sql
final int row = sqlSession.insert("com.atheima.dao.UserDao.saveUser", user);
//3.提交事务
sqlSession.commit();
//4.释放资源
sqlSession.close();
return row;
}
-
dao接口
int saveUser(User user);
-
测试类
@Test
public void testSaveUser() throws Exception{
//3.创建dao
UserDao userDao = new UserDaoImpl(builder.build(in));
User user = new User();
user.setUsername("啥问题");
user.setAddress("曹县");
user.setBirthday(new Date());
user.setSex("男");
userDao.saveUser(user);
}
mybatis编写dao的实现类---修改删除等其他操作
这些操作和前面的都差不了多少,可以查看IDEA中的代码
mybatis编写dao的实现类的执行过程分析---查询方法
- 全套代码分析流程看--截图
我们启动debug模式对finfAll()方法的代码去向溯源,我们可以发现mybatis只是对jdbc进行了一层一层封装,sql语句最终的执行还是由jdbc的预处理对象preperedStatement.executer()方法完成的
PreparedStatementHandler
从执行流程可以看出来,不管是增删改操作最后都汇聚到执行PreparedStatementHandler
类中的update方法,并且由update方法中的preparedStatement中的execute方法具体执行
mybatis中使用代理dao执行流程的分析
- 使用动态代理dao执行流程的全部分析
properties标签的使用和细节(将连接信息放在外部属性文件中,然后引入)
- 外部配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis1?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=888888
- 著配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis著配置文件-->
<configuration>
<!--
配置properties标签
可以在标签内配置连接数据库的信息。也可以通过属性引用外部配置的属性文件
resource属性:
常用于指定配置文件的位置,按照类路径的写法来写,且必须存在于类路径下
-->
<!--r-->
<properties url="file:D:\ideaprojects\trationmybattis\src\main\resources/jdbcConfig.properties" >
<!--也可以使用使用类路径 resource=jdbc.properties-->
<!--配置连接的信息-->
<!--我们可以在这里写,然后再下面进行引用,但是写在配置文件中然后引用更常见-->
<!--<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="888888"/>-->
</properties>
<!--mysql环境的配置-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<!--连接池的配置-->
<dataSource type="POOLED">
<!--配置连接的信息-->
<!--引入配置文件中的信息即可-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--配置映射文件的位置-->
<mappers>
<!--使用xml配置文件-->
<!--我们的mapper也可以使用url属性定位映射文件的位置,前面的道理相同-->
<mapper resource="com/itheima/dao/userDao.xml"></mapper>
</mappers>
</configuration>
typeAliases标签和package标签
- 所以我们其实也可以给实体类起别名
- 注意:是在主配置文件中指定的
mybatis第三天
第1章 Mybatis 连接池与事务深入
内容介绍
连接池介绍
连接池就像一个容器,可以将我们的连接初始化放在一个容器里面,我们要用的时候只需要到容器中取就可以了
当取出一个后剩下的将按照队列一样位置全部往前缩进一位
mybatis连接池的介绍
在传统的JDBC中中我们获取连接由DeiverManager接口负责,但是使用DeiverManager来获取连接效率太低了。现在一般使用连接池来管理连接。java提供了DataSourse接口来管理连接池的连接
mybatis中采用unpooled配置连接池的原理分析
通过查看UNPOOLED模型的实现类UnPooledDataSource中关于获取连接的方法,我们可以知道,每次获取连接都需要注册驱动,获取连接,最后是否连接。没有使用到连接池
mybatis中采用pooled配置连接池的原理分析
总体思路就是:见上图
mybatis中的事务原理和自动提交设置
所以其实我们是可以自己手动设置是否自动提交的,如果不设置则默认是关闭自动提交
- 在创建sqlSession对象的时候可以设置时候自动提交
第二章mybatis映射文件的SQL深入
这个一般是针对查询语句的,当我们的查询条件不同,将不同的查询结果
mybatis中的动态sql语句--if标签
只能使用 and不能使用&&
- 映射文件
- 测试
//4.根据传入的条件动态查询
@Test
public void testFindByCondition() throws Exception{
User user = new User();
user.setUserName("老王");
// user.setUseraddRess("北京");//也可用于地址
final List<User> users = userDao.findByCondition(user);
for (User u : users) {
System.out.println(u);
}
}
mybatis中的动态sql语句--where标签的使用
我们可以使用where标签来替代where1=1这个条件
mybatis中的动态sql语句--foreach和sql标签
我们需要解决的是如何将传入的参数集合赋值给sql语句中in()这个集合中
- 映射文件
<select id="findUserInIds" parameterType="com.athuima.domain.QueryVo" resultType="com.athuima.domain.User">
select * from user
<where>
<if test="ids!=null and ids.size()>0">
<foreach collection="ids" open=" and id in(" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
- 测试
//根据queryVo中的id集合实现查询用户列表
@Test
public void findUserInIds() throws Exception{
QueryVo vo= new QueryVo();
List<Integer> ids = new ArrayList<>();
ids.add(41);
ids.add(42);
ids.add(43);
vo.setIds(ids);
final List<User> users = userDao.findUserInIds(vo);
for (User u : users) {
System.out.println(u);
}
}
- sql标签--抽取重复的ql语句
第三章mysql多表关联查询
mybatis的多表关联查询
完成account表的建立及实现单表查询
建立用户表和账户表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT '用户名称',
`birthday` DATETIME DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) DEFAULT NULL COMMENT '性别',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`username`,`birthday`,`sex`,`address`) VALUES (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`ID` INT(11) NOT NULL COMMENT '编号',
`UID` INT(11) DEFAULT NULL COMMENT '用户编号',
`MONEY` DOUBLE DEFAULT NULL COMMENT '金额',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)-- 和用户表的用户编号绑定外键
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `account`(`ID`,`UID`,`MONEY`) VALUES (1,46,1000),(2,45,1000),(3,46,2000);
完成查询所有账户表
- 主配置文件中指定映射文件的位置
<mapper resource="com/athuima/dao/accountDao.xml"></mapper>
- 映射配置文件
<mapper namespace="com.athuima.dao.AccountDao">
<!--1.查询所有-->
<select id="findAll" resultType="com.athuima.domain.Account">
select * from account;
</select>
</mapper>
- dao接口
/**
* 查询所有账户
* @return
*/
List<Account>findAll();
- 测试
@Test
public void testSelect(){
final List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
完成account一对一操作--通过写account子类方式查询
但是现在我们的要求就是查询所有账户,同时还要获得当前账户所属用户的信息
,其实就是实现多表查询
这个问题本质上是在进行多表查询的时候,怎样对查询结果进行封装的问题
- 我们现在需要取出账户表的全部信息,已经每个账户对应的用户的姓名和地址信息
我们通过创建子类的方法开拓展实体类,使得其可以封装多表查询的结果,但是这种方法现在并不多见 - 实体类(继承了Account类)
package com.athuima.domain;
public class AccountUser extends Account{
private String username;
private String address;
public AccountUser() {
}
public AccountUser(String username, String address) {
this.username = username;
this.address = address;
}
/**
* 获取
* @return username
*/
public String getUsername() {
return username;
}
/**
* 设置
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 获取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 设置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return super.toString()+ "AccountUser{username = " + username + ", address = " + address + "}";
}
}
- 映射文件
<!--查询所有账户同时包含用户名和地址信息-->
<select id="findAllAccountUser" resultType="com.athuima.domain.AccountUser">
select a.*,u.username,u.address
from user u join account a on u.id=a.uid;
</select>
- 测试
@Test
public void findAllAccountUser(){
final List<AccountUser> accounts = accountDao.findAllAccountUser();
for (Account account : accounts) {
System.out.println(account);
}
}
完成account一对一操作--建立实体类关系的方式
我们的每个订单它属于一个用户,在表中我们是根据在订单表中设置外键来表示的。在实体类中我们可以在订单表中放置用户的引用来表示这一点
我们的表和实体类会根据属性和列进行自动封装(我们要指定封装的类型),但是当我们在account中添加user对象,这个对象就不能封装可进行了。所以我们必须要为这个查询专门设计resultMap
- account类(关联一个user对象,实现一对一关系)
package com.athuima.domain;
import java.io.Serializable;
//账户表对应的实体类
public class Account implements Serializable {
private Integer id;//账户id
private Integer uid;//用户id
private Double money;//账户余额
private User user;//一个账户属于一个用户
public Account() {
}
public Account(Integer id, Integer uid, Double money, User user) {
this.id = id;
this.uid = uid;
this.money = money;
this.user = user;
}
/**
* 获取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 设置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 获取
* @return uid
*/
public Integer getUid() {
return uid;
}
/**
* 设置
* @param uid
*/
public void setUid(Integer uid) {
this.uid = uid;
}
/**
* 获取
* @return money
*/
public Double getMoney() {
return money;
}
/**
* 设置
* @param money
*/
public void setMoney(Double money) {
this.money = money;
}
/**
* 获取
* @return user
*/
public User getUser() {
return user;
}
/**
* 设置
* @param user
*/
public void setUser(User user) {
this.user = user;
}
public String toString() {
return "Account{id = " + id + ", uid = " + uid + ", money = " + money + ", user = " + user + "}";
}
}
- 映射文件
<!--配置结果映射关系-->
<resultMap id="accountmap" type="com.athuima.domain.Account">
<!--主键-->
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--用于指定表方关联的引用实体属性-->
<association property="user" javaType="com.athuima.domain.User"><!--javaType用于指定关联属性的类型-->
<id property="id" column="id"></id>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
</association>
</resultMap>
<!--查询所有账户同时包含用户名和地址信息-->
<select id="findAllAccountUser" resultMap="accountmap" >
select a.*,u.username,u.address
from user u join account a on u.id=a.uid;
</select>
- 测试
@Test
public void findAllAccountUser(){
final List<AccountUser> accounts = accountDao.findAllAccountUser();
for (Account account : accounts) {
System.out.println(account);
}
}
一对多查询操作
要求查询出所有用户和他们对应的账户信息。一个用户可能对应一对多的关系,我们需要在实体类中实现这个关系
- 映射文件
<!--建立user表的结果映射-->
<resultMap type="com.athuima.domain.User" id="userMap">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!-- collection 是用于建立一对多中集合属性的对应关系
ofType 用于指定集合元素的数据类型
-->
<collection property="accounts" ofType="com.athuima.domain.Account">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
</collection>
</resultMap>
<!--1.查询所有-->
<select id="findAll" resultMap="userMap" >
select u.*,a.id as aid ,a.uid,a.money from user u left outer join account
a on u.id =a.uid
</select>
说明:resultMap的格式(特别是属性的位置还是按照上面的格式写会比较号好),因为可能会出现无法将多个账户封装到一个集合的情况
- user(和账户关联一对多的关系)
package com.athuima.domain;
import java.util.Date;
import java.util.List;
/**
* @author SWT
* @date 2023/12/09
*/
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//一个用户可能对应多个账户
private List<Account> accounts;
public User() {
}
public User(Integer id, String username, Date birthday, String sex, String address, List<Account> accounts) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
this.accounts = accounts;
}
/**
* 获取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 设置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 获取
* @return username
*/
public String getUsername() {
return username;
}
/**
* 设置
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 获取
* @return birthday
*/
public Date getBirthday() {
return birthday;
}
/**
* 设置
* @param birthday
*/
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
/**
* 获取
* @return sex
*/
public String getSex() {
return sex;
}
/**
* 设置
* @param sex
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* 获取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 设置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
/**
* 获取
* @return accounts
*/
public List<Account> getAccounts() {
return accounts;
}
/**
* 设置
* @param accounts
*/
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public String toString() {
return "User{id = " + id + ", username = " + username + ", birthday = " + birthday + ", sex = " + sex + ", address = " + address + ", accounts = " + accounts + "}";
}
}
- 测试
@Test
public void testSelect(){
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
- 直接执行sql语句的情况
分析mybatis的多对多的步骤并搭建环境
- 角色即身份的意思
多对多--准备角色表的实体类和映射配置
- 添加role表并插入数据
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`ID` INT(11) NOT NULL COMMENT '编号',
`ROLE_NAME` VARCHAR(30) DEFAULT NULL COMMENT '角色名称',
`ROLE_DESC` VARCHAR(60) DEFAULT NULL COMMENT '角色描述',
PRIMARY KEY (`ID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) VALUES (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');
- 插入中间表--- user--role
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`UID` INT(11) NOT NULL COMMENT '用户编号',
`RID` INT(11) NOT NULL COMMENT '角色编号',
PRIMARY KEY (`UID`,`RID`),
KEY `FK_Reference_10` (`RID`),
CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user_role`(`UID`,`RID`) VALUES (41,1),(45,1),(41,2);
查询角色获取角色下属用户信息
多对多是一种表的关系的体现,多对多可以看成是2个一对多,且中间表的列和其他2个表存在外键约束,
因为要查询出来所有用户所以我们需要左外连
- 映射文件
<!--定义 role 表的 ResultMap-->
<resultMap id="roleMap" type="com.athuima.domain.Role">
<id property="roleId" column="id"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="com.athuima.domain.User">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="roleMap">
SELECT
r.*,u.id uid,
u.username username,
u.birthday birthday,
u.sex sex,
u.address address
FROM
ROLE r
INNER JOIN
USER_ROLE ur
ON ( r.id = ur.rid)
INNER JOIN
USER u
ON (ur.uid = u.id);
</select>
- role类
```java
package com.athuima.domain;
import java.io.Serializable;
import java.util.List;
//角色表对应的实体类
public class Role implements Serializable {
private Integer roleId;//角色id
private String roleName;//角色名
private String roleDesc;//角色描述
//多对多关系
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public Role() {
}
public Role(Integer roleId, String roleName, String roleDesc) {
this.roleId = roleId;
this.roleName = roleName;
this.roleDesc = roleDesc;
}
/**
* 获取
* @return roleId
*/
public Integer getRoleId() {
return roleId;
}
/**
* 设置
* @param roleId
*/
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
/**
* 获取
* @return roleName
*/
public String getRoleName() {
return roleName;
}
/**
* 设置
* @param roleName
*/
public void setRoleName(String roleName) {
this.roleName = roleName;
}
/**
* 获取
* @return roleDesc
*/
public String getRoleDesc() {
return roleDesc;
}
/**
* 设置
* @param roleDesc
*/
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
", users=" + users +
'}';
}
}
- 测试
public void testSelect() throws Exception{
//5.使用代理对象执行方法
final List<Role> roles = roleDao.findAll();
//6.遍历结果集
for (Role role : roles) {
System.out.println(role);
}
}
注意看结果体现了多对多的关系
查询用户--获取用户所包含的角色信息
我们的sql语句如果强调使用左右连接可能需要改,其他情况和前面相比,我们的sql都不用修改。我们只需要在user类中添加多对多关系
- user类
package com.athuima.domain;
import java.util.Date;
import java.util.List;
/**
* @author SWT
* @date 2023/12/09
*/
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//多对多关系 :一个用户有多个角色
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public User() {
}
public User(Integer id, String username, Date birthday, String sex, String address) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
/**
* 获取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 设置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 获取
* @return username
*/
public String getUsername() {
return username;
}
/**
* 设置
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 获取
* @return birthday
*/
public Date getBirthday() {
return birthday;
}
/**
* 设置
* @param birthday
*/
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
/**
* 获取
* @return sex
*/
public String getSex() {
return sex;
}
/**
* 设置
* @param sex
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* 获取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 设置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
/**
* 获取
* @return accounts
*/
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", roles=" + roles +
'}';
}
}
- 映射文件
<!--建立user表的结果映射-->
<resultMap type="com.athuima.domain.User" id="userMap">
<id column="uid" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<collection property="roles" ofType="com.athuima.domain.Role">
<id property="roleId" column="id"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
</collection>
</resultMap>
<!--1.查询所有用户,并且查出用户所有的角色信息-->
<select id="findAll" resultMap="userMap" >
SELECT
r.*,u.id uid,
u.username username,
u.birthday birthday,
u.sex sex,
u.address address
FROM
ROLE r
INNER JOIN
USER_ROLE ur
ON ( r.id = ur.rid)
INNER JOIN
USER u
ON (ur.uid = u.id);
</select>
补充内容--JNDI
补充--JDNI的概述和原理
- JNDI模仿windows的注册表
补充--JDNI搭建maven的war工程
补充--测试JNDI数据源的使用以及以及使用细节
原理还不是很懂,好像这个用的也不多
第四天
今日课程安排
延迟加载和立即加载的概念
我们已查询用户就会把该用户100个账户都查询出来,这对内存无疑是巨大的开销
应该需要的是当我们不需要使用账户的时候就应该把账户查询出来,但是又带来一个问题,那我们需要用的时候不就使用不了了吗
mybatis一对一实现延迟加载
在实际开发中一对一和多对一应该是使用的延迟加载,这里只是举一个例子
- 查询账户,实现延迟加载用户
延迟加载根据用到才会去加载,才会查询数据库获取数据,如果没有用到关联的数据,不会去查询数据库
- 主配置文件中:配置mybatis延迟加载
<settings>
<!--开启mybatis延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--允许触发方法进行立即加载 ,否则按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
- 映射配置文件
<!--配置结果映射关系-->
<resultMap id="accountmap" type="com.athuima.domain.Account">
<!--主键-->
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--用于指定表方关联的引用实体属性-->
<!--select指定的内容:查询用户的唯一标识-->
<!--column属性指定的内容:用户根据id查询时,所需要参数的值-->
<association property="user" column="uid" javaType="com.athuima.domain.User" select="com.athuima.dao.UserDao.findById"><!--javaType用于指定关联属性的类型-->
<!--按照默认封装-->
</association>
</resultMap>
<!--1.查询所有-->
<select id="findAll" resultMap="accountmap">
select * from account;
</select>
- 账户类
package com.athuima.domain;
import java.io.Serializable;
//账户表对应的实体类
public class Account implements Serializable {
private Integer id;//账户id
private Integer uid;//用户id
private Double money;//账户余额
private User user;//一个账户属于一个用户
public Account() {
}
public Account(Integer id, Integer uid, Double money, User user) {
this.id = id;
this.uid = uid;
this.money = money;
this.user = user;
}
/**
* 获取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 设置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 获取
* @return uid
*/
public Integer getUid() {
return uid;
}
/**
* 设置
* @param uid
*/
public void setUid(Integer uid) {
this.uid = uid;
}
/**
* 获取
* @return money
*/
public Double getMoney() {
return money;
}
/**
* 设置
* @param money
*/
public void setMoney(Double money) {
this.money = money;
}
/**
* 获取
* @return user
*/
public User getUser() {
return user;
}
/**
* 设置
* @param user
*/
public void setUser(User user) {
this.user = user;
}
public String toString() {
return "Account{id = " + id + ", uid = " + uid + ", money = " + money ;
}
}
- 测试类1
mybatis一对多实现延迟加载
思想是:在需要使用的时候去调用对方配置文件中的配置来实现查询的功能
- 配置文件
<resultMap type="com.athuima.domain.User" id="userMap">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!-- collection 是用于建立一对多中集合属性的对应关系
ofType 用于指定集合元素的数据类型
-->
<collection property="accounts" column="id" ofType="com.athuima.domain.Account" select="com.athuima.dao.AccountDao.findAccountsByUid">
</collection>
</resultMap>
<!--1.查询所有-->
<select id="findAll" resultMap="userMap" >
select * from user;
</select>
- 账户dao映射配置文件
<!--1.根据用户id查询账户-->
<select id="findAccountsByUid" resultType="com.athuima.domain.Account">
select * from account where uid=#{id}
</select>
测试部分和前面的一样
缓存的概念
适用于缓存的举例:淘宝中商品的库存数,可能真实的数据库中名没有商品了,但是缓存中显示还有商品,但是造成的后果影响不大
mybatis中的一级缓存
当我们查询的是同样的结果,将直接在sqlSession的缓存中提取,而不会到数据库中进行查询
说明我们第二次查询并没有到数据库中查询,而是到sqlSession的缓存中取出的结果,返回了相同的user对象
触发清空一级缓存的情况
使用缓存,缓存和数据库的数据同步是我们需要关注的一个问题
- 我们在2个查询相同内容的中间,使用update方法进行修改,第二次查询的内容将会是怎样的呢*
- 只要是update insert delete操作都更新,不需要和查询的内容相同
mybates的二级缓存
- 二级缓存原理图
二级缓存的配置
在第二次查询的时候,会创建一个新的user对象,会将二级缓存中的数据填充到新创建的对象里面去。虽然没有发起查询,但是却重写创建了一个对象。所有2个user地下并不相等
mybatis注解开发
手动搭建环境
mybatis注解开发测试和使用注意事项
- 注意事项
但是此时同样会报错。也就是注解和使用xml文件,二者不能同时存在
结论:只要你使用注解开发,但是在你的配置文件路径下同时包含了映射配置文件,此时不管你用不用这个映射配置文件,他都会报错(mybatis内部设置的)
mybatis注解开发保存和更新功能
- IUserDao接口
package com.itheima.dao;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface IUserDao {
/**
* 查询所有
* @return
*/
@Select("select * from user" )
List<User>findAll();
/*保存操作*/
@Insert("insert into user values(#{id},#{username},#{birthday},#{sex},#{address})")
void save(User user);
//更新操作
@Update("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}")
void update(User user);
}
- 著配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--引入外部配置文件-->
<properties resource="jdbcConfig.properties"></properties>
<!--将该包里面所有的类都注册别名-->
<typeAliases>
<package name="com.itheima.domain"/>
</typeAliases>
<!--JDBC环境配置-->
<environments default="mysql">
<environment id="mysql">
<!--事务名称-->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--指定该包下面的所有dao接口的映射所在的位置-->
<package name="com.itheima.dao"/>
</mappers>
</configuration>
- 测试类
package com.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/*
*Account表的测试
* */
public class AnnotationCRUDTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
//抽取关闭资源的方法
@After//在测试方法之后执行
public void close() throws Exception {
//提交事务
sqlSession.commit();
sqlSession.close();
in.close();
}
@Before//在测试方法之前执行
//抽取初始化方法
public void init() throws Exception{
//1.读取主配置文件数据
in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.创建工厂
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
//3.利用工厂生成数据库操作对象sqlSession
sqlSession = sqlSessionFactory.openSession();
//4.使用sqlSession生成代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@Test
public void testSelect(){
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testUpdate(){
User user = new User();
user.setId(577);
user.setAddress("北京");
user.setBirthday(new Date());
user.setUsername("张三");
user.setSex("女");
userDao.update(user);
}
@Test
public void testInsert(){
User user = new User();
user.setId(577);
user.setAddress("湖北省十堰市");
user.setBirthday(new Date());
user.setUsername("黑马程序员");
user.setSex("男");
userDao.save(user);
}
}
mybatis注解开发CRUD的其他操作
- dao
package com.itheima.dao;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface IUserDao {
/**
* 查询所有
* @return
*/
@Select("select * from user" )
List<User>findAll();
/*保存操作*/
@Insert("insert into user values(#{id},#{username},#{birthday},#{sex},#{address})")
void save(User user);
//更新操作
@Update("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}")
void update(User user);
//删除
@Delete("delete from user where id=#{id}")
void deleteUser(Integer id);
//查询单个对象
@Select("select * from user where id = #{id}")
User selectOneUser(Integer id);
//对用户名进行模糊查询
@Select("select * from user where username like #{username}")
List<User>selectByUsername(String username);
//返回表中记录条数
@Select("select count(*) from user")
int findTotal();
}
- 测试
package com.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/*
*Account表的测试
* */
public class AnnotationCRUDTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
//抽取关闭资源的方法
@After//在测试方法之后执行
public void close() throws Exception {
//提交事务
sqlSession.commit();
sqlSession.close();
in.close();
}
@Before//在测试方法之前执行
//抽取初始化方法
public void init() throws Exception {
//1.读取主配置文件数据
in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.创建工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.利用工厂生成数据库操作对象sqlSession
sqlSession = sqlSessionFactory.openSession();
//4.使用sqlSession生成代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@Test
public void testSelect() {
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testUpdate() {
User user = new User();
user.setId(577);
user.setAddress("北京");
user.setBirthday(new Date());
user.setUsername("张三");
user.setSex("女");
userDao.update(user);
}
@Test
public void testInsert() {
User user = new User();
user.setId(577);
user.setAddress("湖北省十堰市");
user.setBirthday(new Date());
user.setUsername("黑马程序员");
user.setSex("男");
userDao.save(user);
}
@Test
public void testDelete() {
userDao.deleteUser(577);
}
@Test
public void testOneUser() {
final User user = userDao.selectOneUser(48);
System.out.println(user);
}
@Test
public void testSelectByUsername() {
final List<User> users = userDao.selectByUsername("%王%");
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testFindTotal() {
final int total = userDao.findTotal();
System.out.println(total);
}
}
mybatis注解建立实体类属性和数据库表中列的对应关系
- 配置resultMap结果映射(用于替代xml配置中resultMap标签)
- user类(属性名称和数据库列名不一致)
package com.itheima.domain;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
public User() {
}
public User(Integer userId, String userName, Date userBirthday, String userSex, String userAddress) {
this.userId = userId;
this.userName = userName;
this.userBirthday = userBirthday;
this.userSex = userSex;
this.userAddress = userAddress;
}
/**
* 获取
* @return userId
*/
public Integer getUserId() {
return userId;
}
/**
* 设置
* @param userId
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* 获取
* @return userName
*/
public String getUserName() {
return userName;
}
/**
* 设置
* @param userName
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* 获取
* @return userBirthday
*/
public Date getUserBirthday() {
return userBirthday;
}
/**
* 设置
* @param userBirthday
*/
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
/**
* 获取
* @return userSex
*/
public String getUserSex() {
return userSex;
}
/**
* 设置
* @param userSex
*/
public void setUserSex(String userSex) {
this.userSex = userSex;
}
/**
* 获取
* @return userAddress
*/
public String getUserAddress() {
return userAddress;
}
/**
* 设置
* @param userAddress
*/
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
public String toString() {
return "User{userId = " + userId + ", userName = " + userName + ", userBirthday = " + userBirthday + ", userSex = " + userSex + ", userAddress = " + userAddress + "}";
}
}
package com.itheima.dao;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface IUserDao {
/**
* 查询所有
* @return
*/
@Select("select * from user" )
@Results( id = "usermap",
value = {@Result(id = true,property = "userId",column = "id"),//id用于标记是否为主键(默认为false)
@Result(property = "userName",column = "username"),
@Result(property = "userBirthday",column = "birthday"),
@Result(property = "userSex",column = "sex"),
@Result(property = "userAddress",column = "address")
}
)
List<User>findAll();
//查询单个对象
@Select("select * from user where id = #{id}")
@ResultMap("usermap")//引用前面定义的结果映射
User selectOneUser(Integer id);
//对用户名进行模糊查询
@Select("select * from user where username like #{username}")
@ResultMap({"usermap","selectOneUser"})//引用前面定义的结果映射
List<User>selectByUsername(String username);
}
这样我们的结果集封装就没问题了
mybatis注解开发一对一查询配置
- 在这个里面同时设置是否延迟加载
- dao
public interface IAccountDao {
//查询所有账户
@Select("select * from account")
@Results(id = "accountmap",value = {
//账户结果映射
@Result(id = true,property = "id",column = "id"),
@Result(property = "uid",column = "uid"),
@Result(property = "money",column = "money"),
@Result(property ="user",column = "uid",one=@One(select = "com.itheima.dao.IUserDao.findById",fetchType = FetchType.EAGER ))//指定不延迟加载
}
)
List<Account> findAll();
- 测试
@Test
public void testSelect() {
final List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println(account);
System.out.println(account.getUser());
}
}
他们的配置原理和xml文件中完全一致
mybatis注解开发一对多查询配置
- dao
@Select("select * from user" )
@Results( id = "usermap",
value = {@Result(id = true,property = "userId",column = "id"),//id用于标记是否为主键(默认为false)
@Result(property = "userName",column = "username"),
@Result(property = "userBirthday",column = "birthday"),
@Result(property = "userSex",column = "sex"),
@Result(property = "userAddress",column = "address"),
//根据用户id查询他的用户
@Result(property = "accounts",column = "id",many = @Many(select = "com.itheima.dao.IAccountDao.findByUid",fetchType = FetchType.EAGER ))
}
)
List<User>findAll();
- 测试
@Test
public void testSelect() {
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
System.out.println("该用户对应的账户是----------------------------------------------");
System.out.println(user.getAccounts());
System.out.println("-------------------------------------------------------------------------------");
}
}
mybatis注解开发使用二级缓存
一级缓存 自动开启的