Mybatis框架
mybatis是一个半ORM框架,它可以让程序员只关注sql语句
,其他jdbc步骤不用关心.
Mybatis框架: 是方便开发==Dao层(持久层)==代码.
注意:官网: mybatis – MyBatis 3 | 简介
完全的ORM框架,可以实现"全自动",即只要根据ORM要求,将数据表和实体类设计的完全对应,那么在项目中就可以不用写SQL,由框架自动生成.
例如: Hibernate , Jpa , MybatisPlus , tk-mapper
使用流程
1.创建maven-java项目
2.加入对应依赖
<dependencies>
<!-- mybatis的依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- jdbc驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
mybatis全局配置文件
名称:常用mybatis-config.xml或者sqlconfing.xml等
格式:xml格式
位置:mian 的resource下
内容
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 根标签 固定的 -->
<configuration>
<!-- jdbc环境 -->
<environments default="development">
<environment id="development">
<!-- 事务管理:jdbc管理 -->
<transactionManager type="JDBC"/>
<!-- 数据源配置:
type:POOLED,池化技术,使用数据库连接池
-->
<!--
jdbc连接属性
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/java2212"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 加载映射文件 -->
<!--加载接口文件 加载sql语句以及接口方法 -->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
mybatis框架简化dao层,并改名为mapper
package com.qf.mapper;
import com.qf.model.User;
/**
* 现在使用了Mybatis后,统一将包名替换成mapper,接口名替换成XxxMapper.java
*/
public interface UserMapper {
User findUserById(int id);
}
mybatis 的映射文件
<!--
以前,是写完Dao层接口,需要写Dao层实现类,在实现类中写JDBC
现在,是写完Mapper层接口,需要写Mapper接口的xml映射文件,其中写sql语句
-->
<!--
映射文件名称: 模块名Mapper.xml
映射文件格式: xml
映射文件位置: 放在resources下,(后续也可以放在mapper包下...)
-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
mapper跟标签
namespace="该映射文件对应的接口文件的全限定名"
-->
<!--namespace选择进行下部操作的接口 -- >
<mapper namespace="com.qf.mapper.UserMapper">
<!--mysql的增删改查均在mappper标签内进行 -->
<!--
select 标签用于查询(后续还有其他标签)
id: 接口中的方法名
resultType: 结果类型,是查询返回的结果集要封装的那个类(这一步就是自动ORM)
parameterType: 接口中方法的参数类型
-->
<select id="findUserById" resultType="com.qf.model.User" parameterType="int">
<!--
以前此处写的 where id = ?
现在此处写 where id = #{},当接口的参数是简单类型时,此处任意,
但是接口的参数是复杂类型是就有限制,后续再说
此时,{}内的与方法参数名一致即可
-->
select * from user where id = #{id}
</select>
</mapper>
测试
1.加入测试依赖
2 在方法上加上@Test注解
3 单元测试的方法,一定要是无返回值,void
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
执行流程
- 通过配置文件逻辑,获得文件流
- 通过流获得SqlSessionFactory
- 通过工厂获得SqlSession
- 通过SqlSession获得接口的代理对象
- 通过接口对象调用方法执行
- 其实执行的是接口方法对应的xml中的标签内的sql语句
1.查询
接口方法的参数是单个
UserMapper
UserMapper.xml
TestMybitis
Users findUserById(int id);
<select id="findUserById" resultType="com.qf.model.Users" parameterType="int">
<!--
以前此处写的 where id = ?
现在此处写 where id = #{},当接口的参数是简单类型
时,此处任意,
但是接口的参数是复杂类型是就有限制,后续再说
此时,{}内的与方法参数名一致即可
-->
select * from users where id = #{id}
</select>
@Test
public void hello() throws Exception {
// 配置文件路径
String resource = "mybatis-config.xml";
// 通过配置文件,获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过流获得SqlSession工厂
// SqlSession就是一次与SQL的交互
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 动态代理的模式
// 可以通过接口得到对应的映射文件,从而让映射文件执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Users users = mapper.findUserById(1);
System.out.println(users);
}
接口方法的参数是多个
Users findUserByLogin(@Param("name") String name ,@Param("password") String password);
<select id="findUserByLogin" resultType="com.qf.model.Users">
select * from users where name = #{name} and password = #{password}
</select>
@Test
public void find() throws IOException {
// 配置文件路径
String resource = "mybatis-config.xml";
// 通过配置文件,获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过流获得SqlSession工厂
// SqlSession就是一次与SQL的交互
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 动态代理的模式
// 可以通过接口得到对应的映射文件,从而让映射文件执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Users userByLogin = mapper.findUserByLogin("李四", "123");
System.out.println(userByLogin);
}
接口方法的参数是对象类型
Users findUserByUsers(Users users);
<select id="findUserByUsers" resultType="com.qf.model.Users" parameterType="com.qf.model.Users">
select * from users where name = #{name} and password = #{password}
</select>
@Test
public void user() throws IOException {
// 配置文件路径
String resource = "mybatis-config.xml";
// 通过配置文件,获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过流获得SqlSession工厂
// SqlSession就是一次与SQL的交互
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 动态代理的模式
// 可以通过接口得到对应的映射文件,从而让映射文件执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Users userByUsers = mapper.findUserByUsers(new Users(1,"李四","123"));
System.out.println(userByUsers);
}
接口方法的参数是Map
Users findUserByLoginMap(Map<String,String> map);
<select id="findUserByLoginMap" resultType="com.qf.model.Users" parameterType="Map">
select * from users where name = #{name} and password = #{password}
</select>
@Test
public void mapUsers() throws IOException {
// 配置文件路径
String resource = "mybatis-config.xml";
// 通过配置文件,获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过流获得SqlSession工厂
// SqlSession就是一次与SQL的交互
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 动态代理的模式
// 可以通过接口得到对应的映射文件,从而让映射文件执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, String> map = new HashMap<>();
map.put("name","李四");
map.put("password","123");
Users userByLoginMap = mapper.findUserByLoginMap(map);
System.out.println(userByLoginMap);
}
接口方法的参数是List
List<User> findAllUser();
<!--
查询返回一个对象,还是List集合,resultType指定的都是要封装的实体类类型
特别注意:不是List
-->
<select id="findAllUser" resultType="com.qf.model.User">
select * from user
</select>
删除【重要】
int deleteUserById(int id);
<delete id="deleteUserById" parameterType="int">
<!-- 删除使用<delete>标签
没有resultType属性,默认返回int类型数据-->
delete from users where id = #{id}
</delete>
@Test
public void deleteUsers() throws IOException {
// 配置文件路径
String resource = "mybatis-config.xml";
// 通过配置文件,获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过流获得SqlSession工厂
// SqlSession就是一次与SQL的交互
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到SqlSession
// mybatis在执行增,删,改的时候默认开启事务,但是没有自动提交
// 如果在获得SqlSession时,指定了开启自动提交,那么执行增删改时就会自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 动态代理的模式
// 可以通过接口得到对应的映射文件,从而让映射文件执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 根据id进行删除
int i = mapper.deleteUserById(2);
System.out.println(i);
}
更新【重要】
int updateUsersById(Users users);
<!-- 使用update标签-->
<update id="updateUsersById" parameterType="com.qf.model.Users">
update users set name = #{name} , password = #{password} where id = #{id}
</update>
@Test
public void updateUsers() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.updateUsersById(new Users(4, "老六", "456"));
System.out.println(i);
//提交
sqlSession.commit();
}
插入【重要】,主键回填
int insertUsers(Users users);
<!-- 使用insert标签-->
<insert id="insertUsers" parameterType="com.qf.model.Users">
insert into users(name,password) values (#{name},#{password})
<!-- keyProperty : 装载id的属性
order : 顺序,是指在insert前或者后执行该句话
resultType : 结果类型
解释: 在插入数据后,执行该句话,将主键查询到
将其赋值给User类的id属性 -->
<selectKey keyProperty="id" order="AFTER" resultType="int">
select last_insert_id()
</selectKey>
</insert>
@Test
public void insertUsers() throws IOException {
// 配置文件路径
String resource = "mybatis-config.xml";
// 通过配置文件,获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过流获得SqlSession工厂
// SqlSession就是一次与SQL的交互
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到SqlSession
// mybatis在执行增,删,改的时候默认开启事务,但是没有自动提交
// 如果在获得SqlSession时,指定了开启自动提交,那么执行增删改时就会自动提交
SqlSession sqlSession = sqlSessionFactory.openSession();
// 动态代理的模式
// 可以通过接口得到对应的映射文件,从而让映射文件执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Users users = new Users();
users.setName("狗蛋");
users.setPassword("123");
int i = mapper.insertUsers(users);
System.out.println(i);
//也可以进行提交
sqlSession.commit();
//此时对象中的id有值
System.out.println(users);
}
2.全局配置文件设置
2.1配置的属性以及顺序
注意:这是在mybatis-config.xml中的配置
- configuration(配置)
- properties(属性)
settings
(设置)- typeHandlers(类型处理器)
- objectFactory(对象工厂)
plugins
(插件)environments
(环境配置)-
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
mappers
(映射器)
properties设置
使用properties设置来加载数据库配置文件
,可以将数据库连接信息放在独立的配置文件中,方便后期维护.
位置: 在resources下
名字: 任意,一般有db.properties
或者jdbc.properties
文件格式: properties格式,该文件格式是k=v形式
用于连接数据库
需要加载资源(db.properties)
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/java2212?useSSL=false
jdbc.username = root
jdbc.password = 123456
与数据库进行对接
<!-- 使用外部properties文件-->
<properties resource="db.properties" />
....
<!--
jdbc连接属性,使用properties文件中的内容,
使用${key}取出value
-->
<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>
typeAliases设置
用于简写:映射文件时,resultType或者parameterType时,如果参数是类,就需要写全限定名,路径较长.
在mybatis的全局配置文件中使用typeAliases设置类型别名即可
<typeAliases>
<!-- 类型别名 数量过多不适用 -->
<typeAlias type="com.qf.model.User" alias="User"/>
</typeAliases>
推荐使用
<!-- 类型别名 -->
<typeAliases>
<package name="com.qf.model"/>
</typeAliases>
mapper设置
将mapper.xml放在resources下面,可以将映射文件(xml)和接口文件放在一起
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B2CjGrsz-1664149081672)(C:/Users/张海涛/Desktop/zz2212/讲师每日笔记/day53/day53_mybatis.assets/image-20220921104024740.png)]
这样设置以后,我们mappers中加载位置也要变化
DEA默认只会加载resources下面的xml文件,现在xml文件在java/com/qf/mapper下,就无法将其编译加载至项目中.
需要进行绑定
<build>
<!--IDEA的maven项目中,默认源代码目录下(java下)的xml等资源文件并不会
在编译的时候一块打包进classes文件夹,而是直接舍弃掉。
方法1:将xml或properties等配置文件放到resource下,并修
改获取配置文件的代码,比如注册mapper.xml的位置等;
方法2:如果执意java下,那么就需要在maven中添加过滤:
将java下的xml过滤-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>*.xml</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
<mappers>
<!-- 加载映射文件 -->
<mapper resource="com/qf/mapper/UserMapper.xml"/>
</mappers>
当加载映射文件较多时,可以采用直接加载全部文件的方式
但是这样做有前提:
1 接口和映射文件必须放在同一包下
2 接口和映射文件必须同名
否则就会绑定失败,导致无法执行SQL!
<mappers>
<!-- 这样就可以将com.qf.mapper下的所有映射文件全部加载 -->
<package name="com.qf.mapper"/>
</mappers>
运行可能会存在bug
1.是因为idea的编码格式未设置
需要设置为UTF-8
settings——Editor-------File Encodings
全设置为UTF-8
2.pom中加设置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
3.日志
mybatis框架内部使用了日志框架,默认使用的slf4j日志框架.slf4j是日志门面,真正实现日志的是log4j框架.现在就在项目中使用log4j.
使用流程
1.加入依赖
<!-- 日志依赖 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.写配置文件
位置:resources下
名称:log4j.properties
3 运行即可发现控制台有很多日志,可以通过调整级别控制输出的内容
按如上配置好,日志会比较多,但是一般只关注SQL的运行情况,所有我们可以配置日志只打印SQL部分.
# 日志有级别: 从小到大的级别(输出内容由详至简)
# debug < info < warn < error
log4j.rootLogger=error
# 设置mapper接口类或者配置文件中mapper的路径为debug
log4j.logger.com.qf.mapper=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
日志使用
1 使用log4j将日志输出到本地磁盘文件
在log4j.properties文件中设置输出到本地的磁盘的模板
log4j.logger.com.qf.mapper=debug,B
# 将日志输出到本地磁盘
log4j.appender.B = org.apache.log4j.DailyRollingFileAppender
#存放到本地磁盘文件的路径
log4j.appender.B.File = E://000_J_A_V_A//Java2212//第三阶段//day54//mybatis.log
log4j.appender.B.Append = true
#确定日志等级
log4j.appender.B.Threshold = DEBUG
log4j.appender.B.layout = org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss} method:%l%n%m%n
2 使用log4j将日志输出到mysql表[了解]
流程
1.创建mysql表(固定模板)
CREATE TABLE `log` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`project_name` varchar(255) DEFAULT NULL COMMENT '目项名',
`create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',
`level` varchar(255) DEFAULT NULL COMMENT '优先级',
`category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',
`file_name` varchar(255) DEFAULT NULL COMMENT '输出日志消息产生时所在的文件名称 ',
`thread_name` varchar(255) DEFAULT NULL COMMENT '日志事件的线程名',
`line` varchar(255) DEFAULT NULL COMMENT '号行',
`all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
`message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',
PRIMARY KEY (`log_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
2.设置properties文件,构建mysql模板
log4j.logger.com.qf.mapper=debug,logDB
### 输出到mysql
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/java2212
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=123456
log4j.appender.logDB.Sql=INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('day54_mybatis','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
3 自己的代码中使用log4j记录日志
通过log4j记录
改变sout调试代码的方式,弥补只能输出到控制台的短板
package com.qf;
import org.apache.log4j.Logger;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class UserServiceImpl {
// 1 获得日志对象
private static Logger logger = Logger.getLogger(UserServiceImpl.class);
public void testLog(){
// 2使用日志对象记录日志
logger.debug("DEBUG-查询返回对象User");
logger.info("info-查询返回对象User");
logger.warn("warn-查询返回对象User");
try {
}catch (Exception e){
logger.error(e.getMessage());
}
}
}
4.多表联查
4.1一对一查表
model
注意:
一定是将另个对象作为当前对象的属性,而不是集合作为
重写getset方法
toString方法
package com.qf.model;
import java.util.Date;
public class Activity {
private int aid;
private Date atime;
private String asubject;
private String aintr;
private String aaddress;
private Double aprice;
private User user;
public Activity() {
}
public Activity(int aid, Date atime, String asubject, String aintr, String aaddress, Double aprice, User user) {
this.aid = aid;
this.atime = atime;
this.asubject = asubject;
this.aintr = aintr;
this.aaddress = aaddress;
this.aprice = aprice;
this.user = user;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public int getAid() {
return aid;
}
public void setAid(int aid) {
this.aid = aid;
}
public Date getAtime() {
return atime;
}
public void setAtime(Date atime) {
this.atime = atime;
}
public String getAsubject() {
return asubject;
}
public void setAsubject(String asubject) {
this.asubject = asubject;
}
public String getAintr() {
return aintr;
}
public void setAintr(String aintr) {
this.aintr = aintr;
}
public String getAaddress() {
return aaddress;
}
public void setAaddress(String aaddress) {
this.aaddress = aaddress;
}
public Double getAprice() {
return aprice;
}
public void setAprice(Double aprice) {
this.aprice = aprice;
}
@Override
public String toString() {
return "Activity{" +
"aid=" + aid +
", atime=" + atime +
", asubject='" + asubject + '\'' +
", aintr='" + aintr + '\'' +
", aaddress='" + aaddress + '\'' +
", aprice=" + aprice +
", user=" + user +
'}';
}
}
mapper
因为是一对一查表,返回的数据不止一个,所以方法返回的是集合
ActivityMapper
package com.qf.mapper;
import com.qf.model.Activity;
import java.util.List;
public interface ActivityMapper {
//多表联查 查全部
List<Activity> findActivityWithUser();
}
select中
id:方法名
resultMap:手动注入的对象名字
resultMap标签
id:接收手动注入的对象
type:接收手动注入的对象的类型
id标签代表id
result标签代表其他属性
column代表虚拟表列名
property代表对象属性
限用于一对一查表
association:解析对象,
javaType:对象内属性
ActivityMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mapper.ActivityMapper">
<resultMap id="activity" type="Activity">
<id column="aid" property="aid"/>
<result column="atime" property="atime"/>
<result column="asubject" property="asubject"/>
<result column="aintr" property="aintr"/>
<result column="aaddress" property="aaddress"/>
<result column="aprice" property="aprice"/>
<association property="user" javaType="com.qf.model.User">
<id column="uid" property="uid"/>
<result column="uname" property="uname"/>
<result column="upassword" property="upassword"/>
<result column="urealname" property="urealname"/>
<result column="uaddTime" property="uaddTime"/>
<!-- <result column="id" property="aid"/>-->
</association>
</resultMap>
<select id="findActivityWithUser" resultMap="activity">
select
a.aid,
a.atime,
a.asubject,
a.aintr,
a.aaddress,
a.aprice,
u.uid,
u.uname,
u.upassword,
u.urealname,
u.uaddTime,
u.aid id
from activity a , user u where a.aid = u.aid
</select>
</mapper>
Testmybatis
@Test
public void selectUserBYUid1() throws IOException {
//获取文件输出流
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//创建工厂并进行绑定
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
//获取sqlSession
SqlSession sqlSession = build.openSession();
// 可以通过接口得到对应的映射文件,从而让映射文件执行
ActivityMapper mapper = sqlSession.getMapper(ActivityMapper.class);
List<Activity> activityWithUser = mapper.findActivityWithUser();
for (Activity activity : activityWithUser) {
System.out.println(activity);
}
}
4.2一对多查询
model
注意:
一定是将存入另一个对象的集合作为当前对象的属性
package com.qf.model;
import java.util.Date;
import java.util.List;
public class User {
private int uid;
private String uname;
private String upassword;
private String urealname;
private Date uaddTime;
private int aid;
private List<Activity> activityList;
public User() {
}
public User(int uid, String uname, String upassword, String urealname, Date uaddTime, int aid, List<Activity> activityList) {
this.uid = uid;
this.uname = uname;
this.upassword = upassword;
this.urealname = urealname;
this.uaddTime = uaddTime;
this.aid = aid;
this.activityList = activityList;
}
public String getUrealname() {
return urealname;
}
public void setUrealname(String urealname) {
this.urealname = urealname;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpassword() {
return upassword;
}
public void setUpassword(String upassword) {
this.upassword = upassword;
}
public Date getUaddTime() {
return uaddTime;
}
public void setUaddTime(Date uaddTime) {
this.uaddTime = uaddTime;
}
public int getAid() {
return aid;
}
public void setAid(int aid) {
this.aid = aid;
}
public List<Activity> getActivityList() {
return activityList;
}
public void setActivityList(List<Activity> activityList) {
this.activityList = activityList;
}
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", uname='" + uname + '\'' +
", upassword='" + upassword + '\'' +
", urealname='" + urealname + '\'' +
", uaddTime=" + uaddTime +
", aid=" + aid +
", activityList=" + activityList +
'}';
}
}
mapper
创建一个返回值是对象的方法
package com.qf.mapper;
import com.qf.model.User;
public interface UserMapper {
User findUserById(int uid);
int insertUser(User user);
int deleteUser(int uid);
int updateUser(User user);
//一对多
User selectUserWithActivity(int uid);
}
UserMapper.xml
大致标签以及功能与一对一一致
区别
由于属性是集合
collection标签:解析集合属性
ofType标签:引入对象(本类外)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mapper.UserMapper">
<!-- 查询 id代表方法名-->
<select id="findUserById" resultType="User" parameterType="int">
select * from user where uid = #{uid}
</select>
<insert id="insertUser" parameterType="User">
insert into user(uname,upassword,urealname,uaddTime,aid) values(#{uname},#{upassword},#{urealname},#{uaddTime},#{aid})
</insert>
<delete id="deleteUser" parameterType="int">
delete from user where uid = #{uid}
</delete>
<update id="updateUser" parameterType="User">
update user
<set>
<if test="uname != null">
uname = #{uname},
</if>
<if test="upassword != null">
upassword = #{upassword},
</if>
<if test="urealname != null">
urealname = #{urealname},
</if>
<if test="uaddTime != null">
uaddTime = #{uaddTime},
</if>
<if test="aid != null">
aid = #{aid},
</if>
</set>
where uid = #{uid}
</update>
<!-- 一对多-->
<resultMap id="userWithActivity" type="User">
<id column="uid" property="uid"/>
<result column="uname" property="uname"/>
<result column="upassword" property="upassword"/>
<result column="urealname" property="urealname"/>
<result column="uaddTime" property="uaddTime"/>
<result column="id" property="uid"/>
<collection property="activityList" ofType="com.qf.model.Activity">
<id column="aid" property="aid"/>
<result column="atime" property="atime"/>
<result column="asubject" property="asubject"/>
<result column="aintr" property="aintr"/>
<result column="aaddress" property="aaddress"/>
<result column="aprice" property="aprice"/>
</collection>
</resultMap>
<select id="selectUserWithActivity" resultMap="userWithActivity" parameterType="int">
select
u.uid,
u.uname,
u.upassword,
u.urealname,
u.uaddTime,
a.uid id ,
a.aid,
a.atime,
a.asubject,
a.aintr,
a.aaddress,
a.aprice
from activity a , user u where a.uid = u.uid and u.uid = #{uid}
</select>
</mapper>
Testmybatis
@Test
public void selectUserBYUid2() throws IOException {
//获取文件输出流
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//创建工厂并进行绑定
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
//获取sqlSession
SqlSession sqlSession = build.openSession();
// 可以通过接口得到对应的映射文件,从而让映射文件执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUserWithActivity(1);
System.out.println(user);
for (Activity activity : user.getActivityList()) {
System.out.println(activity);
}
}
5.更新语句中使用动态标签
5.1set+if
set标签与if标签
<update id="updateUserById" parameterType="User">
update user
<!-- set标签会出现set关键字
会将最后一个字段的,去掉
set标签内有内容则自动生成set
-->
<set>
<if test="uname != null ">
uname = #{uname},
</if>
<if test="upassword != null ">
upassword = #{upassword},
</if>
<if test="urealname != null ">
urealname = #{urealname},
</if>
<if test="uaddTime != null ">
uaddTime = #{uaddTime},
</if>
</set>
where id = #{id}
</update>
trim 和if
update user
<!--
trim : 滤空
prefix 在if语句前拼接set
suffix 在trim末尾拼接
prefixOverrides 去除sql语句前面的关键字或者字符
suffixOverrides 如果是最后一个字段 将,逗号掩盖
-->
<trim prefix="set" suffixOverrides=",">
<if test="uname != null">
uname = #{uname},
</if>
<if test="upassword != null">
upassword = #{upassword},
</if>
<if test="urealname != null">
urealname = #{urealname},
</if>
<if test="uaddTime != null">
uaddTime = #{uaddTime},
</if>
</trim>
where uid = #{uid}
</update>
6.拓展(逆向工程(神器))
特点:通数据库表自动生成实体类,接口文件,,CRUD方法以及对应的映射文件,以及文件中写好SQL语句.
CRUD是指在做计算处理时的增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete)几个单词的首字母简写。主要被用在描述软件系统中DataBase或者持久层的基本操作功能。
执行流程
1.加入依赖
<dependencies>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
2.加入插件
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<!--指定资源文件的路径-->
<configuration>
<configurationFile>src\main\resources\generator.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
3.编写配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3">
<!-- 生成 JavaBean 对象重写 toString方法 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/java2212"
userId="root"
password="123456">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- targetProject:生成MODEL类的位置 -->
<javaModelGenerator targetPackage="com.qf.model"
targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.qf.mapper"
targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.qf.mapper"
targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<table tableName="admin"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
<table tableName="user"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
</context>
</generatorConfiguration>
4.运行
Maven -----Plugins------mybatis-generator.generate--------mybatis-generator.generate(双击即可)
6.1缓存(cache-面试)
一级缓存:
SqlSession级别的缓存,缓存的数据只在SqlSession内有效。
一级缓存mybatis已近为我们自动开启,不用我们手动操作,而且我们是关闭不了的!!但是我们可以手动清除缓存。一级缓存是sqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个基于 PerpetualCache的HashMap 本地缓存数据结构,用于缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互不影响的。
原理:
第一次查询用户id为1的用户信息,先去缓存中查询是否有id为1的用户信息,如果没有,从数据库中查询用户信息。得到用户信息后在将用户信息储存到一级缓存中。
如果sqlSession去执行commit操作(插入、更新、删除),清空sqlSession中的一级缓存,保证缓存中始终保存的是最新的信息,避免脏读。
第二次查询用户id为1的用户信息,先去缓存中查询是否有id为1的用户信息,如缓存中有,直接从缓存中获取。
注意:两次查询须在同一个sqlsession中完成,否则将不会走mybatis的一级缓存。在mybatis与spring进行整合开发时,事务控制在service中进行,重复调用两次servcie将不会走一级缓存,因为在第二次调用时session方法结束,SqlSession就关闭了
同一个SqlSession演示,会命中缓存
在执行增删改后,会自动进行缓存清除
@Test
public void hello() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession( );
// 同一个SQLsession下的mapper,会命中缓存
UserMapper mapper1 = sqlSession.getMapper(UserMapper.class);
UserMapper mapper2= sqlSession.getMapper(UserMapper.class);
// 第一次查询
User user1 = mapper1.findUserById(29);
System.out.println(user1 );
System.out.println("-------------------" );
// 第二次查询
User user2 = mapper2.findUserById(29);
System.out.println(user2 );
}
不同的sqlSession,不会命中
@Test
public void hello() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到1号sessio
SqlSession sqlSession1 = sqlSessionFactory.openSession( );
// 得到2号session
SqlSession sqlSession2 = sqlSessionFactory.openSession( );
// 同一个SQLsession下的mapper,会命中缓存
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2= sqlSession2.getMapper(UserMapper.class);
// 第一次查询
User user1 = mapper1.findUserById(29);
System.out.println(user1 );
System.out.println("-------------------" );
// 第二次查询
User user2 = mapper2.findUserById(29);
System.out.println(user2 );
}
二级缓存:
二级缓存与一级缓存原理相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper ( Namespace ),并且可自定义存储源,如 Ehcache。作用域为 namespance 是指对该 namespance 对应的配置文件中所有的 select 操作结果都缓存,这样不同线程之间就可以共用二级缓存。
二级缓存可以设置返回的缓存对象策略:
readOnly=“true”(只读):MyBatis 认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis 为了加快获取数据,直接就会将数据在缓存中的引用交给用户 。不安全,速度快。
readOnly=“false”(读写,默认):MyBatis 觉得获取的数据可能会被修改,MyBatis 会利用序列化或反序列化的技术克隆一份新的数据。安全,速度相对慢。
二级缓存是基于映射文件的缓存(namespace),缓存范围比一级缓存更大,不同的SQLSession可以访问二级缓存的内容。哪些数据放入二级缓存需要自己指定
流程
增删改会清空缓存
1.mybatis-config.xml设置开启缓存
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2.Mapper.xml中指定缓存类型
<!-- 设置缓存的实现类
默认是PerpetualCache来实现缓存
ps: 此处可以指定其他类来实现缓存,例如EHCache
-->
<cache />
3.测试
@Test
public void hello() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到1号SqlSession
SqlSession sqlSession1 = sqlSessionFactory.openSession( );
// 得到2号SqlSession
SqlSession sqlSession2 = sqlSessionFactory.openSession( );
// 得到3号SqlSession
SqlSession sqlSession3 = sqlSessionFactory.openSession( );
// 同一个SQLsession下的mapper,会命中缓存
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
// 第一次查询
User user1 = mapper1.findUserById(29);
System.out.println(user1 );
System.out.println("-------------------" );
// 关流时,才会将数据放入缓存
sqlSession1.close();
System.out.println("-------------------" );
// 第二次查询
User user2 = mapper2.findUserById(29);
System.out.println(user2 );
// 关流时,才会将数据放入缓存
sqlSession2.close();
System.out.println("-------------------" );
// 第三次查询
User user3 = mapper3.findUserById(29);
System.out.println(user3 );
// 关流时,才会将数据放入缓存
sqlSession3.close();
}
Cause: java.io.NotSerializableException
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XiTXkbIq-1664149081673)(C:/Users/张海涛/Desktop/zz2212/讲师每日笔记/day54/day54_mybatis.assets/image-20220922151013626.png)]
$和#的区别
#{}相当于是JDBC中的PrearedStatement预处理语句
在使用时,会将#当成是占位符
字符串等数据赋值时自动拼接引号,可以避免SQL注入
---------
${} 相当于是处理语句,直接原样将数据取出,容易造成SQL注入
$方式一般用于传入数据库对象,例如传入表名。
大多数情况下还是经常使用#,一般能用#的就别用$;但有些情况下必须使用$,例:MyBatis排序时使用order by 动态参数时需要注意,用$而不是#。
// 得到1号SqlSession
SqlSession sqlSession1 = sqlSessionFactory.openSession( );
// 得到2号SqlSession
SqlSession sqlSession2 = sqlSessionFactory.openSession( );
// 得到3号SqlSession
SqlSession sqlSession3 = sqlSessionFactory.openSession( );
// 同一个SQLsession下的mapper,会命中缓存
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
// 第一次查询
User user1 = mapper1.findUserById(29);
System.out.println(user1 );
System.out.println("-------------------" );
// 关流时,才会将数据放入缓存
sqlSession1.close();
System.out.println("-------------------" );
// 第二次查询
User user2 = mapper2.findUserById(29);
System.out.println(user2 );
// 关流时,才会将数据放入缓存
sqlSession2.close();
System.out.println("-------------------" );
// 第三次查询
User user3 = mapper3.findUserById(29);
System.out.println(user3 );
// 关流时,才会将数据放入缓存
sqlSession3.close();
}
> Cause: java.io.NotSerializableException
[外链图片转存中...(img-XiTXkbIq-1664149081673)]
### $和#的区别
```tex
#{}相当于是JDBC中的PrearedStatement预处理语句
在使用时,会将#当成是占位符
字符串等数据赋值时自动拼接引号,可以避免SQL注入
---------
${} 相当于是处理语句,直接原样将数据取出,容易造成SQL注入
$方式一般用于传入数据库对象,例如传入表名。
大多数情况下还是经常使用#,一般能用#的就别用$;但有些情况下必须使用$,例:MyBatis排序时使用order by 动态参数时需要注意,用$而不是#。