环境:
JDK1.8
Mysql5.7
Maven3.6.1
IDEA
如何获取Mybatis?
Maven仓库
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
Github:https://github.com/mybatis/mybatis-3/releases
中文文档:https://mybatis.org/mybatis-3/zh/index.html
数据持久化
为什么需要持久化?
Web项目的开发包括:Dao层、Service层、Controller层
思路:搭建环境——>导入Mybatis——>编写代码——>测试
搭建数据库
CREATE DATABASE `mybatis`;
USE mybatis;
CREATE TABLE user(
`id` int(10) PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`password` VARCHAR(255) NOT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO user() VALUES
(1," lisi","li12345"),
(2,"wangwu","wang12345"),
(3,"zhangsan","zhang12345");
新建项目
新建一个普通的maven项目
删除src目录
导入maven依赖
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
资源扫描器插件
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
编写mybatis的配置文件
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql//localhost/mybatis?useSSL=false&
useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="zhangsan"/>
<property name="password" value="zhang12345"/>
dataSource>
environment>
environments>
<mappers>
<package name="com.rui.dao"/>
mappers>
configuration>
编写mybatis的工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return getSession(false);
}
//autoCommit,为false表示为非自动提交事务,与getSqlSession()一致。
//默认为false,true为自动提交
public static SqlSession getSqlSession(boolean autoCommit){
return sqlSessionFactory.openSession();
}
}
实体类
public class User {
private int id;
private String name;
private String password;
public User() {}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
dao接口
public interface UserDao {
//查询所有用户
List<User> selectUsers();
}
dao接口实现类由原来的UserDaoImpl转变为一个Mapper映射文件
<mapper namespace="com.bjpowernode.dao.UserDao">
<select id="selectUsers" resultType="com.bjpowernode.entity.User" >
select * from user;
select>
mapper>
注意:org.apache.ibatis.binding.BindingException: Type interface com.bjpowernode.dao.UserDao is not known to the MapperRegistry.
MapperRegistry是什么?
核心配置文件中配置mappers
junit测试
public class UserDaoTest {
@Test
public void selectUsers(){
//第一步获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//代理对象
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> users = mapper.selectUsers();
users.forEach(System.out::println);
//关闭sqlSession
sqlSession.close();
}
}
可以会遇到的问题
namespace中的包名要和接口名保持一致!
编写接口
public interface UserDao {
//查询所有用户
List<User> selectUsers();
//添加用户
int insertUser(User user);
//更新用户信息
int updateUser(int id);
//删除用户
int delectUser(int id);
}
编写对应得mapper中的sql语句
<mapper namespace="com.bjpowernode.dao.UserDao">
<select id="selectUsers" resultType="com.bjpowernode.entity.User" >
select * from user;
select>
<insert id="insertUser" >
insert into user() values(#{id},#{name},#{password});
insert>
<update id="updateUser">
update user set name="libai",password="libaibuku" where id=#{id};
update>
<delete id="delectUser">
delete from user where id=#{id};
delete>
mapper>
测试
public class UserDaoTest {
@Test
public void selectUsers(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> users = mapper.selectUsers();
users.forEach(System.out::println);
sqlSession.close();
}
@Test
public void insertUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int i = mapper.insertUser(new User(4, "hanxin", "hanxinbuku"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int i = mapper.updateUser(4);
sqlSession.commit();
sqlSession.close();
}
@Test
public void delectUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int i = mapper.delectUser(4);
sqlSession.commit();
sqlSession.close();
}
}
注意点:增删改需要提交事务
对于值传递,有时只需要传递实体对象中的部分属性值。这时就可以用map进行传递
多个参数用map,或者注解!
User toMapSelectUser(Map map);
<select id="toMapSelectUser" resultType="com.bjpowernode.entity.User">
select * from user where name = #{username} and id = #{userid};
select>
@Test
public void toMapSelectUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
HashMap<String, Object> map = new HashMap<>();
map.put("userid", 5);
map.put("username","韩信");
User user = mapper.toMapSelectUser(map);
System.out.println(user);
sqlSession.close();
}
map的key可以自定义,若采用对象则需要固定属性名称
java代码执行的时候,传递通配符%%
List<User> users = mapper.likeSelectUser1("%li%");
在sql拼接中使用通配符【建议使用!】
<select id="likeSelectUser" resultType="com.bjpowernode.entity.User">
select * from user where name like "%"#{name}"%"
</select>
mybatis-config.xml
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息
严格按照顺序声明,否则报错
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
学会使用配置多套运行环境!
Mybatis默认的事务管理器是JDBC,连接池:POOLED
我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的 子元素中设置
在xml文件中,规定了声明顺序
(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,
reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
编写一个配置文件
jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
username=zhangsan
password=zhang12345
在核心配置文件中映入
<properties resource="jdbc.properties"/>
<properties resource="jdbc.properties">
<property name="username" value="zhangsan"/>
<property name="password" value="zhang12345"/>
properties>
第一种方式:为指定的类,设置别名
<typeAliases>
<typeAlias type="com.bjpowernode.entity.User" alias="user"/>
typeAliases>
第二种方式:通过指定包名,为包下所有的实体类设置别名,例如:
扫描实体类的包,它的默认别名就为这个类的类名,首字母小写
<typeAliases>
<package name="com.bjpowernode.entity"/>
typeAliases>
在实体类比较少的时侯,使用第一种方式
如果实体类十分多的时候,建议使用第二种方式
第一种方式可以自定义别名,第二种不行,如果一定要修改可以采用注解的形式进行修改。
@Alias("user")
public class User {
...
}
mybatis内置的别名
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | 未设置 |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
MapperRegistry:注册绑定我们的Mapper文件
方式一:使用声明映射文件位置进行绑定
<mappers>
<mapper resource="com/bjpowernode/dao/UserDao.xml"/>
mappers>
方式二:使用class文件绑定注册
<mappers>
<mapper class="com.bjpowernode.dao.UserDao"/>
mappers>
注意点
接口和他的Mapper配置文件必须同名!
接口和他的Mapper配置文件必须在同一个包下!
方式三:使用扫描包进行注入绑定
<mappers>
<package name="com.bjpowernode.dao"/>
mappers>
注意点
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
SqlSessionFactory:
可以理解为:数据库连接池
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例!
SqlSessionFactory 的最佳作用域是应用作用域。
最简单的就是使用单例模式或者静态单例模式。
SqlSession:
问题:解决属性名与字段名不一致的问题?
解决方法:
<select id="selectUsers" resultType="user" >
select id naem pwd as password from user;
select>
id name pwd
id name password
<resultMap id="UserMap" type="user">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
resultMap>
<select id="selectUsers" resultMap="UserMap" >
select id naem pwd from user;
select>
resultMap
元素是 MyBatis 中最重要最强大的元素如果一个数据库操作,出现了异常,我们需要排错。日志就是最好的助手!
曾经:sout . debug
现在:日志工厂!
在Mybatis中具体使用那个一日志实现,在设置中设定!
在mybatis核心配置文件中,配置我们的日志!
配置:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
什么是Log4j?
通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
我们也可以控制每一条日志的输出格式
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
第一步:导包
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
第二步:log4j.properties
#将等级为DEBUG的日志信息输出到console和fiLe这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
1og4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/mybatis.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.1ogger.org.mybatis=DEBUG
1og4j.logger.java.sq1=DEBUG
log4j.logger.java.sq1.statement=DEBUG
log4j.1ogger.java.sq1.ResultSet=DEBUG
log4j.1ogger.java.sq1.PreparedStatement=DEBUG
第三步:配置日志实现
第四步:Log4j的使用
简单使用
static Logger logger = Logger.getLogger(UserDaoTest.class);
logger.info("info:进入了test()");
logger.debug("debug:进入了test()");
logger.error("error:进入了test()");
为什么要分页?
分页确实有效,但它一定会加大系统的复杂度,但可否不分页呢?如果数据量少的话当然可以.但是对于企业信息系统来说数据量不会限制在一个小范围内.如果不顾一切的Select * from某个表,再将返回的数据一古脑的扔给客户,即使客户能够忍受成千上万足够让人眼花缭乱的表格式数据,繁忙的网络,紧张的服务器也会提出它们无声的抗议,甚至有时会以彻底的罢工作为终结.这个结局有点像古代为所欲为的暴君和他忍无可忍的臣民之间的故事.
程序员不是暴君,他希望程序使生活变得更好而不是更糟.考虑到企业信息系统多是三层甚至更多层架构的事实,程序员在向客户展示数据时都应该采取分页的形式.如果他不想被抱怨淹没或是半夜被电话惊醒的话.
Limit分页
语法:
Select * from user limit startIndex,pageSize;
Select * from user limit pageSize;//0-pageSize
接口
// 数据分页
List<User> toLimitSelectUser(@Param("startIndex") int startIndex,@Param("pageSize") int pageSize);
mapper
<select id="toLimitSelectUser" resultType="user">
select * from user limit #{startIndex},#{pageSize};
select>
测试
@Test
public void toLimitSelectUser(){
//数据分页
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> users = mapper.toLimitSelectUser(0, 2);
users.forEach(System.out::println);
sqlSession.close();
}
1.maven坐标(导入依赖)
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.1.10version>
dependency>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor" />
plugins>
@Test
public void selectUsers(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
PageHelper.startPage(1,2);
List<User> users = mapper.selectUsers();
users.forEach(System.out::println);
sqlSession.close();
}
注意:在mapper映射文件中的SQL语句不能 “;” 结尾,会发生以下情况
select id,name,password from user; LIMIT ?
除了PageHelper.startPage方法外,还提供了类似用法的PageHelper.offsetPage方法。在你需要进行分页的MyBatis,查询方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。
关于接口的理解
接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
接口的本身反映了系统设计人员对系统的抽象理解。
接口应有两类:
一个体有可能有多个抽象面。抽象体与抽象面是有区别的。
三个面向区别
注解在接口方法上声明
//查询所有用户
@Select("select * from user")
List<User> toAnnotationSelectUsers();
需要在核心配置文件中绑定接口!
<mappers>
<package name="com.bjpowernode.dao"/>
mappers>
测试
@Test
public void toAnnotationSelectUsers(){
//数据分页
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> users = mapper.toAnnotationSelectUsers();
users.forEach(System.out::println);
sqlSession.close();
}
注:使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
#{}:进行的是预编译,可以避免SQL注入
${}:使用的是字符串拼接,存在SQL注入的风险
获取并加载全局配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
实例化SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
调用sqlSEssionFactoryBuilder.build(inputStream)中
创建XMLConfigBuilder对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
parser.parse()//解析了配置文件,返回实例化Configuration对象
最终返回SqlSessionFactory实例
调用sqlSessionFactory.openSession()中
创建事务对象
Transaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
创建了执行器对象
Executor executor = this.configuration.newExecutor(Transaction, ExecutorType);
创建sqlSession对象
SqlSession var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
最终返回SqlSession实例对象
UserDao mapper = sqlSession.getMapper(UserDao.class);
在getMapper()中,通过动态代理、反射,实例化了接口对象的代理对象
执行CRUD操作
是否事务回滚
提交事务
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
java library
automatically plugs
build tools
with one annotation your class
导入依赖
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.12version>
<scope>providedscope>
dependency>
在实体类上加注解
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
@UtilityClass
说明
Data:无参构造、get、set、toString、hashCode、equals
@AllArgsConstructor:有参
@NoArgsConstructor:无参
@EqualsAndHashCode:equals和hashCode
@ToString:toString
@Getter:get
@Setter:set
扭曲的审美,爱的隐患
扭曲的审美,导致了被审视的对象处于亚健康状态。使用Lombok插件之后,我们的代码也处于“亚健康”状态。还是回归一开始的那句话:所有的源代码很多时候是用来阅读的,只有很少的时间是用来执行的。
1.JDK版本问题
当我想要将现有项目的JDK从Java 8升级到Java 11时,我发现Lombok不能正常工作了。于是我不得不将所有的Lombok注解从项目源代码中清除,并使用IDE自带的功能生成getter/setter,equals,hashCode,toString以及构造器等方法,你也可以使用Delombok工具完成这一过程。但这终究会消耗你很多的时间。
2.胁迫使用
当你的源代码中使用了Lombok,恰好你的代码又被其他的人所使用,那么依赖你代码的人,也必须安装Lombok插件(不管他们喜不喜欢),同时还要花费时间去了解Lombok注解的使用情况,如果不那么做,代码将无法正常运行。使用过Lombok之后,我发现这是一种很流氓的行为。
3.可读性差
Lombok隐藏了JavaBean封装的细节,如果你使用@AllArgsConstructor注解,它将提供一个巨型构造器,让外界有机会在初始化对象时修改类中所有的属性。首先,这是极其不安全的,因为类中某系属性我们是不希望被修改的;另外,如果某个类中有几十个属性存在,就会有一个包含几十个参数的构造器被Lombok注入到类中,这是不理智的行为;其次,构造器参数的顺序完全由Lombok所控制,我们并不能操控,只有当你需要调试时才发现有一个奇怪的“小强”在等着你;最后,在运行代码之前,所有JavaBean中的方法你只能想象他们长什么样子,你并不能看见。
4.代码耦合度增加
当你使用Lombok来编写某一个模块的代码后,其余依赖此模块的其他代码都需要引入Lombok依赖,同时还需要在IDE中安装Lombok的插件。虽然Lombok的依赖包并不大,但就因为其中一个地方使用了Lombok,其余所有的依赖方都要强制加入Lombok的Jar包,这是一种入侵式的耦合,如果再遇上JDK版本问题,这将是一场灾难。
5.得不偿失
使用Lombok,一时觉得很爽,但它却污染了你的代码,破坏了Java代码的完整性,可读性和安全性,同时还增加的团队的技术债务,这是一种弊大于利,得不偿失的操作。如果你确实想让自己的代码更加精炼,同时又兼顾可读性和编码效率,不妨使用主流的Scala或Kotlin这一基于JVM的语言。
总结
Lombok本身是一个优秀的Java代码库,它采用了一种取巧的语法糖,简化了Java的编码,为Java代码的精简提供了一种方式,但在使用此代码库时,需要了解到Lombok并非一个标准的Java库。使用Lombok,会增加团队的技术债务,降低代码的可读性,增大代码的耦合度和调式难度。虽然在一定程度上Lombok减少了样板代码的书写,但也带来了一些未知的风险。如果你正在参与一个团队项目(或大型项目),考虑到后续的升级与扩展,是否使用Lombok,请与你的团队多沟通和三思。
多个学生对应一个老师,相应的一个老师对应一个学生
对于学生而言,关联,多对一
对于老师而言,集合,一对多
搭建环境
use mybatis;
CREATE TABLE teacher(
id int(10) PRIMARY KEY,
name VARCHAR(10) not NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8
insert into teacher() VALUE
(1,"lisi");
CREATE TABLE student(
id int PRIMARY key,
name VARCHAR(10) not NULL,
tid int not NULL,
FOREIGN KEY(tid) REFERENCES teacher (id)
)ENGINE=INNODB DEFAULT CHARSET=utf8
insert into student() VALUES
(1,"wangwu",1),(2,"zhangsan",1),(3,"xiaoming",1),
(4,"xiaohong",1),(5,"kangkang",1);
构建项目
创建实体类
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
@Data
public class Teacher {
private int id;
private String name;
private List<Student> studentList;
}
定义接口
编写接口对应的映射文件
绑定映射文件
测试
定义接口
// 查询所有学生,包括老师信息
// Select s.id,s.name,s.tid,t.name tname from student s,teacher t where s.tid = t.id
List<Student> selectStudents();
编写映射文件
第一种:按照嵌套查询处理
<resultMap id="studentTeacher" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" javaType="teacher" column="tid" select="selectTeacher"/>
resultMap>
<select id="selectStudents" resultMap="studentTeacher">
Select * from student
select>
<select id="selectTeacher" resultType="teacher">
Select id,name from teacher where id = #{id}
select>
第二种:按照结果嵌套查询
<resultMap id="studentTeacher" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" javaType="teacher">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
association>
resultMap>
<select id="selectStudents1" resultMap="studentTeacher">
Select s.id id,s.name name,s.tid tid,t.name tname from student s,teacher t where s.tid = t.id
select>
测试
public void selectStudents(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<Student> students = mapper.selectStudents();
students.forEach(System.out::println);
sqlSession.close();
}
回顾Mysql多对一查询方式:
子表查询
连表查询
定义接口
// 查询教师信息,及下面所有学生的信息
// select t.id tid,t.name tname,s.id sid,s.name sname from teacher t,student s where t.id=s.tid
Teacher toIdSelectTeacher(@Param("id") int id);
编写映射文件
第一种:按照嵌套查询处理
<resultMap id="teacherStduent" type="teacher">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
<collection property="studentList" ofType="student" column="id" select="toStudents"/>
resultMap>
<select id="toIdSelectTeacher" resultMap="teacherStduent1">
select * from teacher where id=#{id}
select>
<select id="toStudents" resultType="student">
select * from student where tid = #{tid}
select>
第二种:按照结果嵌套查询
<resultMap id="teacherStduent" type="teacher">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
<collection property="studentList" ofType="student" javaType="ArrayList">
<result column="sid" property="id"/>
<result column="sname" property="name"/>
collection>
resultMap>
<select id="toIdSelectTeacher" resultMap="teacherStduent">
select t.id tid,t.name tname,s.id sid,s.name sname from teacher t,student s where t.id=#{id} and t.id=s.tid
select>
测试
@Test
public void toIdSelectTeacher(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherDao mapper = sqlSession.getMapper(TeacherDao.class);
Teacher teacher = mapper.toIdSelectTeacher(1);
System.out.println(teacher);
sqlSession.close();
}
关联- association【多对一】
集合- collection【一对多】
javaType & ofType
JavaType用来指定实体类中属性的类型
ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型!
注意点:
保证SQL的可读性,尽量保证通俗易懂
注意一对多和多对一中,属性名和字段的问题!
如果问题不好排查错误,可以使用日志,建议使用Log4j
什么是动态SQL:动态SQL就是指根据不同的条件生成不同的SQL语句
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
if
choose (when, otherwise)
trim (where, set)
foreach
环境搭建
CREATE TABLE b1og(
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
)ENGINE=InnoDB DEFAULT CHARSET=utf8
<select id="queryBlogIF" parameterType="map" resultType="b1og">
select * from blog where 1=1
<if test="title != null">
and title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
select>
<select id="queryB1ogChoose" parameterType="map" resu7tType="b1og">
select * from blog
<where>
<choose>
<when test="title != null">
title = {#ititle}
when>
<when test="author != null">
and author = #{author}
when>
<otherwise>
and views = #{views}
otherwise>
choose>
where>
select>
<select>
select * from b1og
<where>
<if test="title != null">
title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
where>
select>
<update id="updateB1og" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
if>
<if test="author != null">
author = #{author}
if>
set>
where id = #{id}
update>
所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
<select id="queryBTogForeach" parameterType="map" resu1tType="blog">
select t from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = #{id}
foreach>
where>
select>
collection:集合
item:集合元素变量
open:开始
close:结尾
separator:分割元素
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了
建议:
有的时候,我们可能会将一些功能的部分抽取出来,方便复用!
<sq1 id="if-title-author">
<if test="title !=null">
title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
sq1>
2. 在需要使用的地方使用Include标签引用即可
<select id="queryB7ogIF" parameterType="map" resultType="blog">
select * from blog
<where>
<include refid="if-title-author"/>
where>
select>
注意事项:
查询:连接数据库,耗资源!
一次查询的结果,给他暂存在一个可以直接取到的地方! -->内存:缓存
我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
什么是缓存[ Cache ]?
存在内存中的临时数据。
将用户经常查询的数据放在缓存(内存)中,用户去 查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
为什么使用缓存?
什么样的数据能使用缓存?
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)。
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存也叫本地缓存:
小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!
—级缓存就是一个Map。
缓存失效的情况:
测试:
1.问题:我们需要将实体类序列化!否则会报错
小结:
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
要在程序中使用ehcache,先要导包!
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.1.0version>
dependency>
在mapper中指定使用我们的ehcache缓存实现!
<cache type="org.mybatis.caches.ehcache.Ehcachecache"/>
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="java.io.tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
ehcache>
Redis数据库来做缓存!
角色:指挥者、图纸、工人、楼房
指挥者 调用 工人 建造房屋
工人 通过 图纸 建造房屋
代码实现:
//房屋实体类
package com.bjpowernode.deom;
public class House {
private String A;
private String B;
private String c;
public House() {
}
public String getA() {
return A;
}
public void setA(String a) {
A = a;
}
public String getB() {
return B;
}
public void setB(String b) {
B = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
@Override
public String toString() {
return "House{" +
"A='" + A + '\'' +
", B='" + B + '\'' +
", c='" + c + '\'' +
'}';
}
}
//图纸
public abstract class Builder {
protected House house = new House();
abstract void buildA();
abstract void buildB();
abstract void buildC();
abstract House buildHouse();
}
//工人
public class Worker extends Builder {
@Override
public void buildA() {
house.setA("打地基");
}
@Override
public void buildB() {
house.setB("搭建");
}
@Override
public void buildC() {
house.setC("粉刷");
}
@Override
public House buildHouse() {
buildA();
buildB();
buildC();
return house;
}
}
//指挥者
public class Director {
private Builder worker;
public Director() {
worker = new Worker();
}
public Director(Builder worker) {
this.worker = worker;
}
public House build(){
return worker.buildHouse();
}
}
//测试
public class Test {
public static void main(String[] args) {
Director director = new Director();
House build = director.build();
System.out.println(build);
}
}
通过静态内部类方式实现无序配置
public class Food {
private String A="全家桶";
private String B="可乐";
private String c="汉堡";
public Food() {
}
public String getA() {
return A;
}
public void setA(String a) {
A = a;
}
public String getB() {
return B;
}
public void setB(String b) {
B = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
@Override
public String toString() {
return "House{" +
"A='" + A + '\'' +
", B='" + B + '\'' +
", c='" + c + '\'' +
'}';
}
}
public abstract class Builder {
protected Food food = new Food();
abstract Builder buildA();
abstract Builder buildB();
abstract Builder buildC();
abstract Food buildFood();
}
public class Worker extends Builder {
@Override
public Builder buildA() {
food.setA("雪碧");
return this;
}
@Override
public Builder buildB() {
food.setB("鸡翅");
return this;
}
@Override
public Builder buildC() {
food.setC("薯条");
return this;
}
@Override
Food buildFood() {
return food;
}
}
public class Test {
public static void main(String[] args) {
Food build = new Worker().buildA().buildC().buildFood();
System.out.println(build);
}
}
链式编程
优点:
产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则“。
缺点:
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
Builder模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合。
通过静态内部类方式实现无序配置
public class Food {
private String A="全家桶";
private String B="可乐";
private String c="汉堡";
public Food() {
}
public String getA() {
return A;
}
public void setA(String a) {
A = a;
}
public String getB() {
return B;
}
public void setB(String b) {
B = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
@Override
public String toString() {
return "House{" +
"A='" + A + '\'' +
", B='" + B + '\'' +
", c='" + c + '\'' +
'}';
}
}
public abstract class Builder {
protected Food food = new Food();
abstract Builder buildA();
abstract Builder buildB();
abstract Builder buildC();
abstract Food buildFood();
}
public class Worker extends Builder {
@Override
public Builder buildA() {
food.setA("雪碧");
return this;
}
@Override
public Builder buildB() {
food.setB("鸡翅");
return this;
}
@Override
public Builder buildC() {
food.setC("薯条");
return this;
}
@Override
Food buildFood() {
return food;
}
}
public class Test {
public static void main(String[] args) {
Food build = new Worker().buildA().buildC().buildFood();
System.out.println(build);
}
}
链式编程
优点:
产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则“。
缺点:
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。