关于mybatis:Mybits环境搭建

1.编写dao的接口,里边写查询数据库的形象办法 public interface AccountDao { //查问所有账户 public Set<Account> findAll(); //增加账户 public void addAccount(Account account);}2.编写Mybits的外围配置文件(resources下) <?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> <!--配置环境--> <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/ssm?useUnicode=true&amp;characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--引入映射配置文件--> <mappers> <!--用的是注解的形式--> <!--抉择class属性,该属性只扫描一个接口,如果增加新的接口,需再写一个class属性--> <!--<mapper class="com.ssm.dao.AccountDao"></mapper>--> <!--抉择包属性,该属性会扫描这个包下所有的接口--> <package name="com.ssm.dao"/> <!--如果用的是配置文件的形式,用resource属性--> <!--<mapper resource="com/ssm/dao/xxx.xml"></mapper>--> </mappers></configuration>3.用注解或者xml的形式编写映射关系(这里采纳注解) public interface AccountDao { //查问所有账户 @Select("select * from account") public Set<Account> findAll(); //增加账户 @Insert("insert into account (name,money) values (#{name},#{money})") public void addAccount(Account account);}报错:起因:少了数据库驱动解决办法:增加依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency>报错:解决办法:增加代码 ...

April 24, 2021 · 1 min · jiezi

关于springboot:SpringBoot-Mybatis-Druid-PageHelper在多数据源下如何配置并实现分页

前言本篇文章次要讲述的是SpringBoot整合Mybatis、Druid和PageHelper 并实现多数据源和分页。其中SpringBoot整合Mybatis这块,在之前的的一篇文章中曾经讲述了,这里就不过多阐明了。重点是讲述在多数据源下的如何配置应用Druid和PageHelper 。 SpringBoot + Mybatis + Druid + PageHelper学习笔记和最新面试题 Druid介绍和应用在应用Druid之前,先来简略的理解下Druid。 Druid是一个数据库连接池。Druid能够说是目前最好的数据库连接池!因其优良的性能、性能和扩展性方面,深受开发人员的青眼。 Druid曾经在阿里巴巴部署了超过600个利用,通过一年多生产环境大规模部署的严苛考验。Druid是阿里巴巴开发的号称为监控而生的数据库连接池! 同时Druid不仅仅是一个数据库连接池,Druid 外围次要包含三局部: 基于Filter-Chain模式的插件体系。DruidDataSource 高效可治理的数据库连接池。SQLParserDruid的次要性能如下: 是一个高效、功能强大、可扩展性好的数据库连接池。能够监控数据库拜访性能。数据库明码加密取得SQL执行日志扩大JDBC介绍方面这块就不再多说,具体的能够看官网文档。那么开始介绍Druid如何应用。 首先是Maven依赖,只须要增加druid这一个jar就行了。 <dependency>         <groupId>com.alibaba</groupId>         <artifactId>druid</artifactId>         <version>1.1.8</version>  </dependency>配置方面,次要的只须要在application.properties或application.yml增加如下就能够了。 阐明:因为这里我是用来两个数据源,所以略微有些不同而已。Druid 配置的阐明在上面中曾经说的很具体了,这里我就不在阐明了。# 默认的数据源master.datasource.url=jdbc:mysql://localhost:3306/springBoot?useUnicode=true&characterEncoding=utf8&allowMultiQueries=truemaster.datasource.username=rootmaster.datasource.password=123456master.datasource.driverClassName=com.mysql.jdbc.Driver# 另一个的数据源cluster.datasource.url=jdbc:mysql://localhost:3306/springBoot_test?useUnicode=true&characterEncoding=utf8cluster.datasource.username=rootcluster.datasource.password=123456cluster.datasource.driverClassName=com.mysql.jdbc.Driver# 连接池的配置信息# 初始化大小,最小,最大spring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.initialSize=5spring.datasource.minIdle=5spring.datasource.maxActive=20# 配置获取连贯期待超时的工夫spring.datasource.maxWait=60000# 配置距离多久才进行一次检测,检测须要敞开的闲暇连贯,单位是毫秒spring.datasource.timeBetweenEvictionRunsMillis=60000# 配置一个连贯在池中最小生存的工夫,单位是毫秒spring.datasource.minEvictableIdleTimeMillis=300000spring.datasource.validationQuery=SELECT 1 FROM DUALspring.datasource.testWhileIdle=truespring.datasource.testOnBorrow=falsespring.datasource.testOnReturn=false# 关上PSCache,并且指定每个连贯上PSCache的大小spring.datasource.poolPreparedStatements=truespring.datasource.maxPoolPreparedStatementPerConnectionSize=20# 配置监控统计拦挡的filters,去掉后监控界面sql无奈统计,'wall'用于防火墙spring.datasource.filters=stat,wall,log4j# 通过connectProperties属性来关上mergeSql性能;慢SQL记录spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000胜利增加了配置文件之后,咱们再来编写Druid相干的类。 首先是MasterDataSourceConfig.java这个类,这个是默认的数据源配置类。 @Configuration@MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory")public class MasterDataSourceConfig {    static final String PACKAGE = "com.pancm.dao.master";    static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml";    @Value("${master.datasource.url}")    private String url;    @Value("${master.datasource.username}")    private String username;    @Value("${master.datasource.password}")    private String password;    @Value("${master.datasource.driverClassName}")    private String driverClassName;    @Value("${spring.datasource.initialSize}")    private int initialSize;    @Value("${spring.datasource.minIdle}")    private int minIdle;    @Value("${spring.datasource.maxActive}")    private int maxActive;   @Value("${spring.datasource.maxWait}")    private int maxWait;    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")    private int timeBetweenEvictionRunsMillis;    @Value("${spring.datasource.minEvictableIdleTimeMillis}")    private int minEvictableIdleTimeMillis;    @Value("${spring.datasource.validationQuery}")    private String validationQuery;    @Value("${spring.datasource.testWhileIdle}")    private boolean testWhileIdle;    @Value("${spring.datasource.testOnBorrow}")    private boolean testOnBorrow;    @Value("${spring.datasource.testOnReturn}")    private boolean testOnReturn;    @Value("${spring.datasource.poolPreparedStatements}")    private boolean poolPreparedStatements;    @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")    private int maxPoolPreparedStatementPerConnectionSize;    @Value("${spring.datasource.filters}")    private String filters;    @Value("{spring.datasource.connectionProperties}")    private String connectionProperties;    @Bean(name = "masterDataSource")    @Primary    public DataSource masterDataSource() {        DruidDataSource dataSource = new DruidDataSource();        dataSource.setUrl(url);        dataSource.setUsername(username);        dataSource.setPassword(password);        dataSource.setDriverClassName(driverClassName);        //具体配置        dataSource.setInitialSize(initialSize);        dataSource.setMinIdle(minIdle);        dataSource.setMaxActive(maxActive);        dataSource.setMaxWait(maxWait);        dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);        dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);        dataSource.setValidationQuery(validationQuery);        dataSource.setTestWhileIdle(testWhileIdle);        dataSource.setTestOnBorrow(testOnBorrow);        dataSource.setTestOnReturn(testOnReturn);       dataSource.setPoolPreparedStatements(poolPreparedStatements);        dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);        try {            dataSource.setFilters(filters);        } catch (SQLException e) {            e.printStackTrace();        }        dataSource.setConnectionProperties(connectionProperties);        return dataSource;    }    @Bean(name = "masterTransactionManager")    @Primary    public DataSourceTransactionManager masterTransactionManager() {        return new DataSourceTransactionManager(masterDataSource());    }    @Bean(name = "masterSqlSessionFactory")    @Primary    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)            throws Exception {        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();        sessionFactory.setDataSource(masterDataSource);        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()                .getResources(MasterDataSourceConfig.MAPPER_LOCATION));        return sessionFactory.getObject();    }}其中这两个注解阐明下: @Primary :标记这个 Bean 如果在多个同类 Bean 候选时,该 Bean 优先被思考。多数据源配置的时候留神,必须要有一个主数据源,用 @Primary 标记该 Bean。@MapperScan:扫描 Mapper 接口并容器治理。须要留神的是sqlSessionFactoryRef 示意定义一个惟一 SqlSessionFactory 实例。下面的配置完之后,就能够将Druid作为连接池应用了。然而Druid并不简简单单的是个连接池,它也能够说是一个监控利用,它自带了web监控界面,能够很清晰的看到SQL相干信息。 在SpringBoot中使用Druid的监控作用,只须要编写StatViewServlet和WebStatFilter类,实现注册服务和过滤规定。这里咱们能够将这两个写在一起,应用@Configuration和@Bean。 为了不便了解,相干的配置阐明也写在代码中了,这里就不再过多赘述了。 ...

March 28, 2021 · 2 min · jiezi

关于mybatis:给MybatisPlus插上小翅膀支持多表查询

之前始终应用Mybatis-Plus,说实话,集体还是比拟喜爱Mybatis-Plus。 ORM框架用的比拟多的就两个,JPA和Mybatis。据说国内用Mybatis比拟多,国外用JPA比拟多。 而Mybatis-Plus是在Mybatis的根底上,减少了很多牛的性能。 再粘一下官网介绍的个性,又啰嗦了:无侵入:只做加强不做扭转,引入它不会对现有工程产生影响,如丝般顺滑损耗小:启动即会主动注入根本 CURD,性能根本无损耗,间接面向对象操作弱小的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过大量配置即可实现单表大部分 CRUD 操作,更有弱小的条件结构器,满足各类应用需要反对 Lambda 模式调用:通过 Lambda 表达式,不便的编写各类查问条件,无需再放心字段写错反对主键主动生成:反对多达 4 种主键策略(内含分布式惟一 ID 生成器 - Sequence),可自在配置,完满解决主键问题反对 ActiveRecord 模式:反对 ActiveRecord 模式调用,实体类只需继承 Model 类即可进行弱小的 CRUD 操作反对自定义全局通用操作:反对全局通用办法注入( Write once, use anywhere )内置代码生成器:采纳代码或者 Maven 插件可疾速生成 Mapper 、 Model 、 Service 、 Controller 层代码,反对模板引擎,更有超多自定义配置等您来应用内置分页插件:基于 MyBatis 物理分页,开发者无需关怀具体操作,配置好插件之后,写分页等同于一般 List 查问分页插件反对多种数据库:反对 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库内置性能剖析插件:可输入 Sql 语句以及其执行工夫,倡议开发测试时启用该性能,能疾速揪出慢查问内置全局拦挡插件:提供全表 delete 、 update 操作智能剖析阻断,也可自定义拦挡规定,预防误操作具体的能够去官网看:mybatis.plus/ 官网新域名也是牛。反正用过的都说好。 至于JPA,尽管集体感觉有点太死板,不过也有值得学习的中央。 很早以前,用Mybatis-Plus的时候,有一个比拟麻烦的问题,就是如果一组数据存在多张表中,这些表之间可能是一对一,一对多或者多对一,那我要想全副查出来就要调好几个Mapper的查询方法。代码行数一下就减少了很多。 之前也看过Mybatis-Plus的源码,想过如何使Mybatis-Plus反对多表联接查问。可是发现难度不小。因为Mybatis-Plus底层就只反对单表。 最近看到JPA的@OneToOne、@OneToMany、@ManyToMany这些注解,突然一个想法就在我的脑海里闪现进去,如果像JPA那样应用注解的形式,是不是简略很多呢? 当时申明,全是本人想的,没有看JPA源码, 所以实现形式可能和JPA不一样。 说干就干增加注解解决注解打包公布可能有人不晓得,其实Mybatis也是反对拦截器的,既然如此,用拦截器解决注解就能够啦。 注解 One2One@Inherited@Documented@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface One2One { /** * 本类主键列名 */ String self() default "id"; /** * 本类主键在关联表中的列名 */ String as(); /** * 关联的 mapper */ Class<? extends BaseMapper> mapper();}说一下,如果有两张表,A和B是一对一的关系,A表id在B表中是a_id,用这样的形式关联的。 在A的实体类中应用这个注解,self就是id,而as就是a_id,意思就是A的id作为a_id来查问,而mapper就是B的Mapper,上面是例子A就是UserAccount,B就是UserAddress。 ...

March 22, 2021 · 5 min · jiezi

关于mybatis:SpringBoot工程下商品子系统分析及实现

1.业务形容:将数据库中的商品信息从数据库查问进去,而后进行删除、修 改、增加等操作。 2.技术架构设计:整体仍旧基于“分而治之”的设计思维,采纳MVC分层对业务进行技术实现。 3.基于特定架构下技术选型:1)SpringBoot 治理依赖,提供根底配置,实现开箱即用2)HikariCP 定义连接池3)MyBatis 实现数据的长久操作4)Spring IOC 实现资源整合 5)Spring Web 模块实现申请响应解决6)Thymeleaf 基于此对象实现html模板解析 4.外围API(接口和类)的设计1)pojo (Goods)2)dao (GoodsDao,GoodsMapper.xml)3)service(GoodsService,GoodsServiceImpl)4)controller (GoodsController) 商品信息的查问并出现:1)Goods (id,name,remark,createdTime)2)GoodsDao (@Mapper):List<Goods> findGoods();3)GoodsMapper.xml(mapper/goods/GoodsMapper.xml)4)GoodsService,GoodsServiceImpl (@Service)5)GoodsController(@Controller): String doFindGoods(){return goods;}6)goods.html (templates/modules/) 第一步:定义商品pojo对象Goods,基于此对象封装商品数据。 public class Goods { private Integer id; private String name; private String remark; private Date createdTime; @Override public String toString() { return "Goods{" + "id=" + id + ", name='" + name + '\'' + ", remark='" + remark + '\'' + ", createdTime=" + createdTime + '}';}public Integer getId() { return id;}public void setId(Integer id) { this.id = id;}public String getName() { return name;}public void setName(String name) { this.name = name;}public String getRemark() { return remark; }public void setRemark(String remark) { this.remark = remark; }public Date getCreatedTime() { return createdTime; }public void setCreatedTime(Date createdTime) { this.createdTime = createdTime; }}第二步:定义GoodsDao接口及查询方法 ...

March 13, 2021 · 1 min · jiezi

关于mybatis:SpringBoot工程中MyBatis框架的应用

MyBatis 概述1)优良的长久层框架?(长久层:负责将内存中的对象存储到数据库的那一层对象(DAO).2)最大的劣势? (可能低成本的疾速实现数据库中的数据操作)2.1)开源 (收费-成本低)2.2)简略 (操作步骤,参数映射,后果映射)2.4)灵便 (动静SQL-能够更好适配不同需要-for,if,...)2.5)稳固 (不会三天两头出错) 2.MyBatis框架的利用架构?1)DAO (本人写的数据拜访对象-这个对象中要调用mybatis标准-SqlSession)2)MyBatis API(标准,标准的实现-封装了JDBC操作)3)JDBC API (标准-Java官网定义)4)Driver(JDBC标准的实现-数据库厂商提供其驱动) 1)如何了解SqlSession?(mybatis中实现与数据库会话的一个入口对象)2)测试过程中SqlSession标准的具体实现是谁?(SqlSessionTemplate)3)测试过程中咱们本人创立SqlSessionFactory对象了吗?(没有,springboot工程底层创立-开箱即用)4)SqlSession对象获取连贯时,连贯来自哪里?(HikariDataSource关联的连接池对象) Bug?1)IllegalArgumentException (Mapped Statement Collections does not contains .....) MyBatis框架为咱们的接口创立实现类?在GoodsDao接口上增加@Mapper注解或者在启动类上增加@MapperScan("dao接口所在包") 1)@Mapper注解的作用是什么?形容数据逻辑层接口,通知mybatis框架这个接口的实现类,由mybatis创立,并将其实现类的对象交给spring治理,Spring会为他治理的这个bean起个名字,默认为接口名,而后首字母小写。 2)MyBatis为咱们的Dao接口创立的实现类及其办法外部做了什么? a)获取sqlSession对象b)获取sql映射的key信息(namespace,elementId)c)基于statement获取sql映射信息(底层是存储在了MappedStatement对象中)d)基于sqlsession以及sql实现与数据库的会话。 3)数据层接口(XxxDao)能够有多个实现类吗?(能够,然而须要时注入那个须要咱们进行设计) Bug? 1.BindingException (mybatis绑定异样) 1)业务类中应用的日志API是谁?(org.slf4j.Logger)2)日志API的利用中应用到了什么设计模式?(门面模式)3)你理解哪些日志的级别吗?(trace,debug,info,error)4)你晓得日志输入时底层为什么要定义日志级别吗?(便于对日志行为进行关上,敞开操作)5)日志输入时"{}"作用是什么?(占位符,相似sql中的?)6)日志能够记录到文件吗?如何配置?(能够,logging.file.path)7)为什么要将日志的记录放到商品业务逻辑对象中?(日志记录自身就属于一个业务)

March 13, 2021 · 1 min · jiezi

关于mybatis:假装是小白之重学MyBatis一

在工作中发现对MyBatis还是有了解不到位的中央,所以就打算重新学习一下MyBatis。简介MyBatis 是一款优良的长久层框架,它反对自定义 SQL、存储过程以及高级映射。MyBatis 罢黜了简直所有的 JDBC 代码以及设置参数和获取后果集的工作。MyBatis 能够通过简略的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,一般老式 Java 对象)为数据库中的记录。 首先是MyBatis给本人的定位是长久层框架,那什么是长久层,简略的说就是和数据库沟通的那一层,也就是MVC模式下的dao(Data Access Object)层,dao层的代码负责将业务代码变成对数据库表的操作,至于为什么叫长久层,我感觉是跟数据库的持久性特点有关系呢! 在dao层没有任何框架之前,咱们是间接应用原生的jdbc来操纵数据库的数据,拼接SQL,设置参数,获取后果集重复性的工作经常让人非常腻烦,然而JDBC从设计思路上讲也足够优良了,做到了跨数据库,让使用者不用关怀是哪个数据库,而采取不同的操作,JDBC就是一组接口,由各大数据库厂商提供对应的实现类,所以也不大可能做齐全的定制的化操作,所以JDBC的设计思维就是宽泛一点。然而咱们心愿简略点,所以如果你看视频去学习的话,基本上学完JDBC,就会讲如何封装一个工具类JdbcUtils,来防止反复代码的编写,但这并不是一个Java程序员的痛点,对吗?Java社区也关注到了这个问题,开始着手对JDBC进行扩大,进行降级。这也就是MyBatis、Hibernate、Spring Data JPA等ORM框架。 等等你方才又提到了一个名词,ORM框架,那什么是ORM框架? ORM Object Relational Mapping 即对象关系映射,听起来好形象啊! 不要焦急,听我细细道来,Java是一门面向对象的语言,咱们当初广泛应用的数据库是关系型数据库(表为次要模式),ORM的思维就是是否将表映射为对象呢? 一条数据记录就是一个对象。 所以MyBatis是一款优良的长久层、ORM框架,在JDBC的根底上进行扩大、封装,罢黜了简直所有JDBC代码以及设置参数和获取后果集的工作,大大简化了长久层代码开发。 对简化了长久层代码的开发,简略点,简略点,咱们都喜爱简略的货色。 如何学习一门技术?个别状况,学一门框架,最好还是去官网去学,以前我是图速度快,去B站找的视频。当初发现MyBatis官网写的教程挺不错的,更让我喜爱的是有中文版本:好到我让我感觉我这篇博客,是不是还是有必要写。然而思虑再三,还是打算写,官网文档配合本人的了解,让本人的常识更成零碎。 筹备工作要用MyBatis,咱们首先要引入MyBatis,MyBatis是在原生JDBC的根底上做扩大,所以咱们要引入对应的数据库驱动(数据库驱动就是数据库厂商实现的JDBC),本篇咱们应用的是MySQL,Druid来治理数据库连贯。 本篇咱们仍然应用Maven来搭建我的项目: 对应的依赖如下: <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.5</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.30</version> <scope>test</scope> </dependency>slf4j是日志框架,输入的信息会更粗疏,倡议引入。 如果你不会用maven倡议你去学maven,参看我这篇博客: Maven学习笔记,十分通俗易懂的入门。 如果你不想学Maven,想用jar包模式,也行,我的博客就是这么贴心,哈哈哈哈。 首先进入MyBatis官网。 3. 第一个MyBatis 程序首先咱们要建一个配置文件 ...

March 10, 2021 · 6 min · jiezi

关于mybatis:超经典的-25-道-MyBatis-面试题

什么是 Mybatis?MyBatis 的长处MyBatis 框架的毛病MyBatis 框架实用场合MyBatis 与 Hibernate 有哪些不同?#{}和${}的区别是什么?当实体类中的属性名和表中的字段名不一样 ,怎么办 ?含糊查问 like 语句该怎么写?Mapper 接口的工作原理是什么?Mapper 接口里的办法,参数不同时,办法能重载吗?Mybatis 是如何进行分页的?分页插件的原理是什么?Mybatis是如何将sql执行后果封装为指标对象并返回的?都有哪些映射模式?如何执行批量插入?如何获取主动生成的(主)键值?在 mapper 中如何传递多个参数?Mybatis 动静 sql 有什么用?执行原理?有哪些动静 sql?Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否能够反复?为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?一对一、一对多的关联查问 ?MyBatis 实现一对一有几种形式?具体怎么操作的?Mybatis 是否反对提早加载?如果反对,它的实现原理是什么?Mybatis 的一级、二级缓存什么是 MyBatis 的接口绑定?有哪些实现形式?应用 MyBatis 的 mapper 接口调用时有哪些要求?简述 Mybatis 的插件运行原理,以及如何编写一个插件什么是 Mybatis?Mybatis 是一个半 ORM(对象关系映射)框架,它外部封装了 JDBC,开发时 只须要关注 SQL 语句自身,不须要破费精力去解决加载驱动、创立连贯、创立 statement 等繁冗的过程。程序员间接编写原生态 sql,能够严格控制 sql 执行性 能,灵便度高。MyBatis 能够应用 XML 或注解来配置和映射原生信息,将 POJO 映射成数 据库中的记录,防止了简直所有的 JDBC 代码和手动设置参数以及获取后果集。通过 xml 文件或注解的形式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中 sql 的动静参数进行映射生成最终执行的 sql 语句,最 后由 mybatis 框架执行 sql 并将后果映射为 java 对象并返回。(从执行 sql 到返 回 result 的过程)。MyBatis 的长处基于 SQL 语句编程,相当灵便,不会对应用程序或者数据库的现有设计造成任 何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于对立治理;提供 XML 标签,反对编写动静 SQL 语句,并可重用。与 JDBC 相比,缩小了 50%以上的代码量,打消了 JDBC 大量冗余的代码,不 须要手动开关连贯;很好的与各种数据库兼容(因为 MyBatis 应用 JDBC 来连贯数据库,所以只有 JDBC 反对的数据库 MyBatis 都反对)。可能与 Spring 很好的集成;提供映射标签,反对对象与数据库的 ORM 字段关系映射;提供对象关系映射 标签,反对对象关系组件保护。MyBatis 框架的毛病SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写 SQL 语句的功底有肯定要求。SQL 语句依赖于数据库,导致数据库移植性差,不能随便更换数据库。MyBatis 框架实用场合MyBatis 专一于 SQL 自身,是一个足够灵便的 DAO 层解决方案。对性能的要求很高,或者需要变动较多的我的项目,如互联网我的项目,MyBatis 将是 不错的抉择MyBatis 与 Hibernate 有哪些不同?Mybatis 和 hibernate 不同,它不齐全是一个 ORM 框架,因为 MyBatis 须要 程序员本人编写 Sql 语句Mybatis 间接编写原生态 sql,能够严格控制 sql 执行性能,灵便度高,十分 适宜对关系数据模型要求不高的软件开发,因为这类软件需要变动频繁,一但需 求变动要求迅速输入成绩。然而灵便的前提是 mybatis 无奈做到数据库无关性, 如果须要实现反对多种数据库的软件,则须要自定义多套 sql 映射文件,工作量大。Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的 软件,如果用 hibernate 开发能够节俭很多代码,提高效率。{}和${}的区别是什么?============== ...

March 9, 2021 · 3 min · jiezi

关于mybatis:MyBatis的工作原理

Mybatis是一个优良的长久层框架,底层基于JDBC实现与数据库的交互。并在JDBC操作的根底上做了封装和优化,它借助灵便的SQL定制,参数及后果集的映射形式,更好的适应了以后互联网技术的倒退。 MyBatis 能够应用简略的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO为数据库中的记录。 mybatis应用程序通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件(也能够用Java文件配置的形式,须要增加@Configuration)来构建SqlSessionFactory(SqlSessionFactory是线程平安的);而后,SqlSessionFactory的实例间接开启一个SqlSession,再通过SqlSession实例取得Mapper对象并运行Mapper映射的SQL语句,实现对数据库的CRUD和事务提交,之后敞开SqlSession。

March 9, 2021 · 1 min · jiezi

关于mybatis:mybatis一对一-一对多-多对多注配置

一对一 public interface PersonMapper { //依据id查问 @Select("SELECT * FROM person WHERE id=#{id}") public abstract Person selectById(Integer id);}public interface CardMapper { //查问全副 @Select("SELECT * FROM card") @Results({ @Result(column = "id",property = "id"), @Result(column = "number",property = "number"), @Result( property = "p", // 被蕴含对象的变量名 javaType = Person.class, // 被蕴含对象的理论数据类型 column = "pid", // 依据查问出的card表中的pid字段来查问person表 /* one、@One 一对一固定写法 select属性:指定调用哪个接口中的哪个办法 */ one = @One(select = "com.itheima.one_to_one.PersonMapper.selectById") ) }) public abstract List<Card> selectAll();}@Results:封装映射关系的父注解。 Result[] value():定义了 Result 数组@Result:封装映射关系的子注解。 ...

March 8, 2021 · 2 min · jiezi

关于mybatis:mybatis一对一-一对多-多对多xml配置

mybatis一对一 一对多 多对多xml配置

March 8, 2021 · 1 min · jiezi

关于mybatis:mybatis延迟加载举例

开启提早加载 #提早加载总开关mybatis-plus.configuration.lazy-loading-enabled=true#设置按需加载mybatis-plus.configuration.aggressive-lazy-loading=false表关系Mapper文件配置 IAccountDao.xml mapper配置文件<mapper namespace="com.itheima.dao.IAccountDao"> <!-- 定义封装account和user的resultMap --> <resultMap id="accountUserMap" type="account"> <id property="id" column="id"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!-- 一对一的关系映射:配置封装user的内容 select属性指定的内容:查问用户的惟一标识: column属性指定的内容:用户依据id查问时,所须要的参数的值(resultMap中id项的column) property为account实体类中无关association的实体类的名称 javaType示意association中实体类的类型 --> <association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"></association> </resultMap> <!-- 查问所有 一对一用到的 --> <select id="findAll" resultMap="accountUserMap"> select * from account </select> <!-- 依据用户id查问账户列表 一对多用到的 --> <select id="findAccountByUid" resultType="account"> select * from account where uid = #{uid} </select></mapper>IUserDao.xml mapper配置<mapper namespace="com.itheima.dao.IUserDao"> <!-- 定义User的resultMap--> <resultMap id="userAccountMap" type="user"> <id property="id" column="id"></id> <result property="username" column="username"></result> <result property="address" column="address"></result> <result property="sex" column="sex"></result> <result property="birthday" column="birthday"></result> <!-- 配置user对象中accounts汇合的映射 --> <!-- 一对多的关系映射:配置封装account的内容 select属性指定的内容:查问账户的惟一标识: column属性指定的内容:用户依据id查问账户时,所须要的参数的值(resultMap中id项的column) property为user实体类中无关association的实体类的名称 ofType示意association中实体类的类型 --> <collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findAccountByUid" column="id"></collection> </resultMap> <!-- 查问所有 一对多用到的--> <select id="findAll" resultMap="userAccountMap"> select * from user </select> <!-- 依据id查问用户 一对一用到的--> <select id="findById" parameterType="INT" resultType="user"> select * from user where id = #{uid} </select></mapper>实体类 ...

March 8, 2021 · 1 min · jiezi

关于mybatis:mybatis-懒加载中的错误

org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory$EnhancedResultObjectProxyImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)解决方案yml文件中 spring:  jackson:    serialization:     FAIL_ON_EMPTY_BEANS: falseproperties spring.jackson.serialization.fail-on-empty-beans=false敞开该查问的懒加载 fetchType="eager" 上面的两种办法我也试过,如同并没有什么作用,在进行序列化的时候就会呈现问题比如说BackstageRole会变成Backstage_$$_jvst12a_0这种代理对象(非原始对象)。<resultMap id="RoleMap" type="com.hyper.netman.entity.business.BackstageRole"> <result column="id" property="id"/> <result column="role_name" property="roleName"/> <result column="role_desc" property="roleDesc"/> <result column="create_time" property="createTime"/> <result column="modify_time" property="modifyTime"/> <!--须要敞开懒加载,不然对象不是原来的对象--> <collection property="resources" column="id" javaType="list" fetchType="eager" select="getRolesMenus"/></resultMap>返回的类加上注解(即实体类)@JsonIgnoreProperties(value = { "handler" })public class BackstageRole{}

March 8, 2021 · 1 min · jiezi

关于mybatis:分页助手PageHelper的使用

分页助手PageHelper的应用简介pagehelper是一个很好用的mybatis的分页插件,通过这个插件能够十分不便的实现分页性能。 官网地址 应用这个插件的应用形式非常简单。 引入依赖新建一个springboot我的项目,增加以下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><!--mybatis--><dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version></dependency><!--mapper--><dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>2.1.5</version></dependency><!--pagehelper 分页--><dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional></dependency>增加配置#数据库spring.datasource.driverClassName = com.mysql.jdbc.Driverspring.datasource.url = jdbc:mysql://localhost:3306/study_springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8spring.datasource.username = rootspring.datasource.password = root#mybatismybatis.type-aliases-package=com.njit.modelmybatis.mapper-locations=classpath:mapper/*.xml#pagehelperpagehelper.helperDialect=mysqlpagehelper.reasonable=truepagehelper.supportMethodsArguments=truepagehelper.params=count=countSql而后在启动类上指定tk-mapper的包名。 @SpringBootApplication@MapperScan("com.njit.mapper")public class PagehelperDemoApplication { public static void main(String[] args) { SpringApplication.run(PagehelperDemoApplication.class, args); }}增加数据库的实体类@Datapublic class User { private Integer id; private String name; private String password;}编写通用mapperimport com.njit.model.User;import tk.mybatis.mapper.common.Mapper;/** * @Author: njitzyd * @Date: 2021/2/21 22:28 * @Description: UserMapper * @Version 1.0.0 */public interface UserMapper extends Mapper<User> {}测试理论应用中只有在进行数据库查问的后面用静态方法指定分页的参数即可。 ...

February 22, 2021 · 1 min · jiezi

关于mybatis:MyBatis文档-最近更新-07-十月-2020-版本-356

摘自:https://mybatis.org/mybatis-3...简介什么是 MyBatis?MyBatis 是一款优良的长久层框架,它反对自定义 SQL、存储过程以及高级映射。MyBatis 罢黜了简直所有的 JDBC 代码以及设置参数和获取后果集的工作。MyBatis 能够通过简略的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,一般老式 Java 对象)为数据库中的记录。 帮忙改良文档...如果你发现文档有任何的脱漏,或短少某一个性能点的阐明,最好的解决办法是先本人学习,而后为脱漏的部份补上相应的文档。 该文档 xdoc 格局的源码文件可通过我的项目的 Git 代码库来获取。复刻该源码库,作出更新,并提交 Pull Request 吧。 还有其余像你一样的人都须要浏览这份文档,而你,就是这份文档最好的作者。 文档的翻译版本您能够浏览 MyBatis 文档的其余语言版本: EnglishEspañol日本語한국어简体中文想用你的母语来理解 MyBatis 吗?那就将文档翻译成你的母语并提供给咱们吧! 入门装置要应用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类门路(classpath)中即可。 如果应用 Maven 来构建我的项目,则需将上面的依赖代码置于 pom.xml 文件中: <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version></dependency>从 XML 中构建 SqlSessionFactory每个基于 MyBatis 的利用都是以一个 SqlSessionFactory 的实例为外围的。SqlSessionFactory 的实例能够通过 SqlSessionFactoryBuilder 取得。而 SqlSessionFactoryBuilder 则能够从 XML 配置文件或一个事后配置的 Configuration 实例来构建出 SqlSessionFactory 实例。 从 XML 文件中构建 SqlSessionFactory 的实例非常简单,倡议应用类门路下的资源文件进行配置。 但也能够应用任意的输出流(InputStream)实例,比方用文件门路字符串或 file:// URL 结构的输出流。MyBatis 蕴含一个名叫 Resources 的工具类,它蕴含一些实用办法,使得从类门路或其它地位加载资源文件更加容易。 ...

February 8, 2021 · 37 min · jiezi

关于mybatis:学习笔记-MyBatis-MyBatis-开发

官网文档https://mybatis.org/mybatis-3/zh/index.html基于MyBaits开发的过程 1.sqlSession.selectList("EmpMapper.findAll"):SqlSession中封装的操作数据库的办法,MyBatis中操作数据库最次要的对象和所有的办法。2.EmpMapper.findAll:String字符串,办法中要执行的sql语句的地位,即映射的sql语句。3.<mapper namespace="EmpMapper">:所有的sql语句存在的小区4.<select id="findAll" resultType="Ring1270.pra.MyBatis.pojo.Emp">类型 select * from emp</select>:具体sql所在的门牌号和sql自身+后果集问题:封装在Sqlsession对象中的办法在寻找sql的映射时,传入的参数是一个须要拼接的(namespace+id)的字符串,这种形式容易产生字符串拼写错误且编译期间不会提醒的问题,存在肯定的危险。解决形式:Mapper接口开发利用接口作为桥梁,在寻找sql映射时,带着相应的参数先走接口,接口对传来的参数进行了查看判断,同时接口会进行编译也就防止了字符串拼写错误和编译不提醒的问题。基于MyBatis接口开发的过程 1.sqlSession.getMapper(EmpMapper.class):SqlSession中封装的操作数据库的getMapper()`<T> T getMapper(Class<T> type)`办法2.EmpMapper.class:接口,规定了办法名(id),参数和返回值接管类型3.<mapper namespace="Ring1270.pra.MyBatis.Mapper.EmpMapper">:所有的sql语句被规定的小区4.<select id="findAll" resultType="Ring1270.pra.MyBatis.pojo.Emp"> select * from emp</select>:具体sql所在的门牌号和sql自身+后果集类型(和接口中的绝对应)接口Mapperpackage Ring1270.pra.MyBatis.Mapper;import Ring1270.pra.MyBatis.pojo.Emp;import java.util.List;public interface EmpMapper { /** * 依据id查问员工信息 * @param id * @return Emp */ public Emp findById(Integer id); public List<Emp> findAll();}1、创立一个接口,接口的全限定类名和mapper文件的namespace值要雷同2、mapper文件中每条要执行的SQL语句,在接口中要增加一个对应的办法,并且接口中的办法名和SQL标签上的id值雷同3、Mapper接口中办法接管的参数类型,和mapper.xml中定义的sql的接管的参数类型要雷同4、接口中办法的返回值类型和SQL标签上的resultType即返回值类型雷同(如果办法返回值是汇合,resultType只须要指定汇合中的泛型)对于XxxMapper.xml理论开发中的xml文件<?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="xxx.xxx.xxx.xx.dao.OrgUnitsMapper"> <resultMap id="BaseResultMap" type="xxx.xxx.xxx.xx.OrgUnits"> <id column="id" jdbcType="BIGINT" property="id"/> <result column="create_by" jdbcType="BIGINT" property="createBy"/> <result column="create_time" jdbcType="DATE" property="createTime"/> <result column="update_by" jdbcType="BIGINT" property="updateBy"/> <result column="update_time" jdbcType="DATE" property="updateTime"/> <result column="delete_flag" jdbcType="TINYINT" property="deleteFlag"/> <result column="unit_update" jdbcType="TINYINT" property="unitUpdate"/> <result column="import_flag" jdbcType="TINYINT" property="importFlag"/> <result column="company_id" jdbcType="BIGINT" property="companyId"/> <result column="unit_code" jdbcType="VARCHAR" property="unitCode"/> <result column="unit_type" jdbcType="VARCHAR" property="unitType"/> <result column="unit_fname_cn" jdbcType="VARCHAR" property="unitFnameCn"/> <result column="unit_sname_cn" jdbcType="VARCHAR" property="unitSnameCn"/> <result column="unit_fname_en" jdbcType="VARCHAR" property="unitFnameEn"/> <result column="unit_sname_en" jdbcType="VARCHAR" property="unitSnameEn"/> <result column="unit_pid" jdbcType="BIGINT" property="unitPid"/> <result column="unit_scope" jdbcType="VARCHAR" property="unitScope"/> <result column="unit_leveltype" jdbcType="VARCHAR" property="unitLeveltype"/> <result column="unit_empid" jdbcType="BIGINT" property="unitEmpid"/> <result column="unit_chargeid" jdbcType="BIGINT" property="unitChargeid"/> <result column="unit_hcityid" jdbcType="BIGINT" property="unitHcityid"/> <result column="unit_wcityid" jdbcType="BIGINT" property="unitWcityid"/> <result column="unit_cotcid" jdbcType="BIGINT" property="unitCotcid"/> <result column="status" jdbcType="VARCHAR" property="status"/> <result column="unit_project" jdbcType="BIGINT" property="unitProject"/> <result column="unit_sdate" jdbcType="DATE" property="unitSdate"/> <result column="unit_edate" jdbcType="DATE" property="unitEdate"/> <result column="unit_invreason" jdbcType="VARCHAR" property="unitInvreason"/> <result column="comment" jdbcType="VARCHAR" property="comment"/> <result column="unit_oaid" jdbcType="VARCHAR" property="unitOaid"/> <result column="unit_qywxid" jdbcType="VARCHAR" property="unitQywxid"/> <result column="unit_ddid" jdbcType="VARCHAR" property="unitDdid"/> <result column="unit_dlocation" jdbcType="VARCHAR" property="unitDlocation"/> <result column="unit_dlocationX" jdbcType="VARCHAR" property="unitDlocationX"/> <result column="unit_dlocationY" jdbcType="VARCHAR" property="unitDlocationY"/> <result column="unit_ischart" jdbcType="TINYINT" property="unitIschart"/> <result column="unit_posttype" jdbcType="TINYINT" property="unitPosttype"/> <result column="unit_company" jdbcType="BIGINT" property="unitCompany"/> <result column="unit_dept" jdbcType="BIGINT" property="unitDept"/> </resultMap> <sql id="BaseColumnList"> id,create_by,create_time,update_by,update_time,delete_flag,unit_update,import_flag,company_id,unit_code,unit_type,unit_fname_cn,unit_sname_cn,unit_fname_en ,unit_sname_en,unit_pid,unit_scope,unit_leveltype,unit_empid ,unit_chargeid,unit_hcityid,unit_wcityid,unit_cotcid ,status,unit_project,unit_sdate,unit_edate,unit_invreason,comment,unit_oaid,unit_qywxid,unit_ddid ,unit_dlocation,unit_dlocationX,unit_dlocationY,unit_ischart,unit_posttype,unit_company,unit_dept </sql> <select id="selPageByOrgUnits" resultMap="BaseResultMap"> select <include refid="BaseColumnList" /> from org_units where 1=1 <if test="orgUnits != null "> <if test="orgUnits.id!=null and orgUnits.id != '' "> and id = #{orgUnits.id} </if><if test="orgUnits.createBy!=null and orgUnits.createBy != '' "> and create_by = #{orgUnits.createBy} </if><if test="orgUnits.createTime!=null and orgUnits.createTime != '' "> and create_time = #{orgUnits.createTime} </if><if test="orgUnits.updateBy!=null and orgUnits.updateBy != '' "> and update_by = #{orgUnits.updateBy} </if><if test="orgUnits.updateTime!=null and orgUnits.updateTime != '' "> and update_time = #{orgUnits.updateTime} </if><if test="orgUnits.deleteFlag!=null and orgUnits.deleteFlag != '' "> and delete_flag = #{orgUnits.deleteFlag} </if><if test="orgUnits.unitUpdate!=null and orgUnits.unitUpdate != '' "> and unit_update = #{orgUnits.unitUpdate} </if><if test="orgUnits.importFlag!=null and orgUnits.importFlag != '' "> and import_flag = #{orgUnits.importFlag} </if><if test="orgUnits.companyId!=null and orgUnits.companyId != '' and orgUnits.companyId != 0 "> and company_id = #{orgUnits.companyId} </if><if test="orgUnits.unitCode!=null and orgUnits.unitCode != '' "> and unit_code = #{orgUnits.unitCode} </if><if test="orgUnits.unitType!=null and orgUnits.unitType != '' "> and unit_type = #{orgUnits.unitType} </if><if test="orgUnits.unitFnameCn!=null and orgUnits.unitFnameCn != '' "> and unit_fname_cn like concat('%',#{orgUnits.unitFnameCn},'%') </if><if test="orgUnits.unitSnameCn!=null and orgUnits.unitSnameCn != '' "> and unit_sname_cn = #{orgUnits.unitSnameCn} </if><if test="orgUnits.unitFnameEn!=null and orgUnits.unitFnameEn != '' "> and unit_fname_en = #{orgUnits.unitFnameEn} </if><if test="orgUnits.unitSnameEn!=null and orgUnits.unitSnameEn != '' "> and unit_sname_en = #{orgUnits.unitSnameEn} </if><if test="orgUnits.unitPid!=null and orgUnits.unitPid != '' "> and unit_pid = #{orgUnits.unitPid} </if><if test="orgUnits.unitScope!=null and orgUnits.unitScope != '' "> and unit_scope = #{orgUnits.unitScope} </if><if test="orgUnits.unitLeveltype!=null and orgUnits.unitLeveltype != '' "> and unit_leveltype = #{orgUnits.unitLeveltype} </if><if test="orgUnits.unitEmpid!=null and orgUnits.unitEmpid != '' "> and unit_empid = #{orgUnits.unitEmpid} </if><if test="orgUnits.unitChargeid!=null and orgUnits.unitChargeid != '' "> and unit_chargeid = #{orgUnits.unitChargeid} </if><if test="orgUnits.unitHcityid!=null and orgUnits.unitHcityid != '' "> and unit_hcityid = #{orgUnits.unitHcityid} </if><if test="orgUnits.unitWcityid!=null and orgUnits.unitWcityid != '' "> and unit_wcityid = #{orgUnits.unitWcityid} </if><if test="orgUnits.unitCotcid!=null and orgUnits.unitCotcid != '' "> and unit_cotcid = #{orgUnits.unitCotcid} </if><if test="orgUnits.status!=null and orgUnits.status != '' "> and status = #{orgUnits.status} <!-- 失效状态卡失效、生效工夫 --> <if test="orgUnits.status == '02valid'"> and (now() between unit_sdate and unit_edate) <!-- and (date_format(sdate,'%Y-%m') &lt;= date_format(now(),'%Y-%m') and date_format(edate,'%Y-%m') &gt; date_format(now(),'%Y-%m')) --> </if> </if><if test="orgUnits.unitProject!=null and orgUnits.unitProject != '' "> and unit_project = #{orgUnits.unitProject} </if><if test="orgUnits.unitSdate!=null and orgUnits.unitSdate != '' "> and unit_sdate = #{orgUnits.unitSdate} </if><if test="orgUnits.unitEdate!=null and orgUnits.unitEdate != '' "> and unit_edate = #{orgUnits.unitEdate} </if><if test="orgUnits.unitInvreason!=null and orgUnits.unitInvreason != '' "> and unit_invreason = #{orgUnits.unitInvreason} </if><if test="orgUnits.comment!=null and orgUnits.comment != '' "> and comment = #{orgUnits.comment} </if><if test="orgUnits.unitIschart!=null and orgUnits.unitIschart != '' "> and unit_ischart = #{orgUnits.unitIschart} </if> </if> </select> <select id="selectTreeById" resultMap="BaseResultMap"> select <include refid="BaseColumnList" /> from ( SELECT <include refid="BaseColumnList" /> ,@le:= IF (unit_pid = 0 ,0, IF( LOCATE(CONCAT('|',unit_pid,':'), @pathlevel) > 0 ,SUBSTRING_INDEX(SUBSTRING_INDEX(@pathlevel,CONCAT('|',unit_pid,':'),-1),'|',1) +1 ,@le+1)) levels, @pathlevel:= CONCAT(@pathlevel,'|',id,':', @le ,'|') pathlevel, @pathnodes:= IF( unit_pid =0,',0', CONCAT_WS(',', IF( LOCATE(CONCAT('|',unit_pid,':'),@pathall) > 0 ,SUBSTRING_INDEX(SUBSTRING_INDEX(@pathall,CONCAT('|',unit_pid,':'),-1),'|',1) ,@pathnodes ) ,unit_pid ) ) paths, @pathall:=CONCAT(@pathall,'|',id,':', @pathnodes ,'|') pathall FROM org_units, (SELECT @le:=0,@pathlevel:='', @pathall:='',@pathnodes:='') vv where delete_flag=0 <!-- ORDER BY unit_pid,unit_order --> ORDER BY unit_pid ) src <where> (FIND_IN_SET(#{nodeId}, paths) OR id = #{nodeId}) and delete_flag = 0 <if test="status!=null and status != '' "> and status in <foreach collection="status.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> <if test="status == '02valid'"> and (now() between unit_sdate and unit_edate) <!-- and (date_format(sdate,'%Y-%m') &lt;= date_format(now(),'%Y-%m') and date_format(edate,'%Y-%m') &gt; date_format(now(),'%Y-%m')) --> </if> </if> <if test="unitType!=null"> and unit_type in <foreach collection="unitType.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </if> <!-- <if test="unitCode!=null"> and unit_code like '%${unitCode}%' </if> --> #{LangWhere} </where> <!-- order by unit_order --> </select> <select id="selectTreeById2" resultMap="BaseResultMap"> select <include refid="BaseColumnList" /> from org_units <where> delete_flag = 0 <if test="companyId!=null and companyId!='' and companyId!=0"> and company_id = #{companyId} </if> <if test="id!=null"> <!-- and id=${id} --> and id in <foreach collection="id.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </if> <if test="status!=null and status != ''"> and status in <foreach collection="status.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> <if test="status.contains('02valid') "> and (now() between unit_sdate and unit_edate) <!-- and (date_format(sdate,'%Y-%m') &lt;= date_format(now(),'%Y-%m') and date_format(edate,'%Y-%m') &gt; date_format(now(),'%Y-%m')) --> </if> </if> <if test="unitPid!=null and unitPid!=''"> and (unit_pid in <foreach collection="unitPid.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> or id in <foreach collection="unitPid.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> ) </if> <if test="unitTypes!=null"> and unit_type in <foreach collection="unitTypes.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </if>com.hrocloud.znjs.orgemp.model.OrgUnitsChart <if test="unitCode!=null"> and unit_code like concat('%',#{unitCode},'%') </if> </where> </select> <select id="selectByObjIds" resultMap="BaseResultMap"> select <include refid="BaseColumnList" /> from org_units <where> delete_flag = 0 <!-- and (date_format(sdate,'%Y-%m') &lt;= date_format(now(),'%Y-%m') and date_format(edate,'%Y-%m') &gt;= date_format(now(),'%Y-%m')) --> and status = '02valid' and (now() between unit_sdate and unit_edate) <if test="orgUnits.unitPid!=null and orgUnits.unitPid!=''"> and unit_pid in <foreach collection="orgUnits.unitPid.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </if> <if test="orgUnits.unitType!=null and orgUnits.unitType!=''"> and unit_type in <foreach collection="orgUnits.unitType.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </if> </where> </select> <!--查问父节点和子节点--> <select id="selectIdandPid" resultMap="BaseResultMap"> select <include refid="BaseColumnList" /> from org_units <where> delete_flag = 0 and unit_update = 0 <!-- and (date_format(sdate,'%Y-%m') &lt;= date_format(now(),'%Y-%m') and date_format(edate,'%Y-%m') &gt;= date_format(now(),'%Y-%m')) -->-- and status = '02valid' and (now() between unit_sdate and unit_edate) and ( unit_pid in <foreach collection="orgUnits.unitPid.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> or id in <foreach collection="orgUnits.unitPid.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> ) </where> </select> <select id="selectSubUnitById" resultMap="BaseResultMap"> select <include refid="BaseColumnList" /> from org_units where delete_flag = 0 and unit_update = 0 <if test="orgUnits != null "> <if test="orgUnits.id!=null and orgUnits.id != '' "> and id = #{orgUnits.id} </if><if test="orgUnits.qcUnitAuth!=null and orgUnits.qcUnitAuth != '' "> <!-- 数据权限 --> and id in <foreach collection="orgUnits.qcUnitAuth.split(',')" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </if><if test="orgUnits.companyId!=null and orgUnits.companyId != '' and orgUnits.companyId != 0 "> and company_id = #{orgUnits.companyId} </if><if test="orgUnits.unitCode!=null and orgUnits.unitCode != '' "> and unit_code like concat('%',#{orgUnits.unitCode},'%') </if><if test="orgUnits.unitType!=null and orgUnits.unitType != '' "> and unit_type = #{orgUnits.unitType} </if><if test="orgUnits.unitFnameCn!=null and orgUnits.unitFnameCn != '' "> and unit_fname_cn like concat('%',#{orgUnits.unitFnameCn},'%') </if><if test="orgUnits.unitSnameCn!=null and orgUnits.unitSnameCn != '' "> and unit_sname_cn like concat('%',#{orgUnits.unitSnameCn},'%') </if><if test="orgUnits.unitFnameEn!=null and orgUnits.unitFnameEn != '' "> and unit_fname_en like concat('%',#{orgUnits.unitFnameEn},'%') </if><if test="orgUnits.unitSnameEn!=null and orgUnits.unitSnameEn != '' "> and unit_sname_en like concat('%',#{orgUnits.unitSnameEn},'%') </if><if test="orgUnits.unitPid!=null and orgUnits.unitPid != '' "> <choose> <when test="orgUnits.unitType!=null and orgUnits.unitType == '02dept'.toString() "> <!-- 所有上级部门 --> and (FIND_IN_SET(id,fun_getAllSubOrgDept(#{orgUnits.unitPid})) </when> <otherwise> <!-- 所有上级 --> and (FIND_IN_SET(id,fun_getAllSubOrgUnits(#{orgUnits.unitPid})) </otherwise> </choose> <!-- 是否蕴含本人 --> <choose> <when test="orgUnits.qcIncludeSelf == '1'.toString() "> or id=#{orgUnits.unitPid} ) </when> <otherwise> ) </otherwise> </choose> </if><if test="orgUnits.unitLeveltype!=null and orgUnits.unitLeveltype != '' "> and unit_leveltype = #{orgUnits.unitLeveltype} </if><if test="orgUnits.unitEmpid!=null and orgUnits.unitEmpid != '' "> and unit_empid = #{orgUnits.unitEmpid} </if><if test="orgUnits.unitChargeid!=null and orgUnits.unitChargeid != '' "> and unit_chargeid = #{orgUnits.unitChargeid} </if><if test="orgUnits.unitHcityid!=null and orgUnits.unitHcityid != '' "> and unit_hcityid = #{orgUnits.unitHcityid} </if><if test="orgUnits.unitWcityid!=null and orgUnits.unitWcityid != '' "> and unit_wcityid = #{orgUnits.unitWcityid} </if><if test="orgUnits.unitCotcid!=null and orgUnits.unitCotcid != '' "> and unit_cotcid = #{orgUnits.unitCotcid} </if><if test="orgUnits.unitIschart!=null and orgUnits.unitIschart != '' "> and unit_ischart = #{orgUnits.unitIschart} </if><if test="orgUnits.status!=null and orgUnits.status != '' "> and status = #{orgUnits.status} <!-- 失效状态卡失效、生效工夫 --> <if test="orgUnits.status == '02valid'.toString()"> <choose> <when test="orgUnits.qcDate!=null and orgUnits.qcDate !='' "> and (Date(#{orgUnits.qcDate}) between unit_sdate and unit_edate) </when> <otherwise> and (now() between unit_sdate and unit_edate) </otherwise> </choose> </if> </if><if test="orgUnits.unitProject!=null and orgUnits.unitProject != '' "> and unit_project = #{orgUnits.unitProject} </if> </if> </select> <!-- 2020-12-04 darren add 用于获取看板总公司-营销中心部门 --> <select id="selectSubUnitForKBZGSYXZXDETP" resultMap="BaseResultMap"> select <include refid="BaseColumnList" /> from org_units where delete_flag = 0 and unit_update = 0 <if test="orgUnits != null "> <if test="orgUnits.unitType!=null and orgUnits.unitType != '' "> and unit_type = #{orgUnits.unitType} </if><if test="orgUnits.unitPid!=null and orgUnits.unitPid != '' "> <choose> <when test="orgUnits.unitType!=null and orgUnits.unitType == '02dept'.toString() "> <!-- 所有上级部门 --> and (FIND_IN_SET(id,fun_getAllSubOrgDept(#{orgUnits.unitPid})) </when> <otherwise> <!-- 所有上级 --> and (FIND_IN_SET(id,fun_getAllSubOrgUnits(#{orgUnits.unitPid})) </otherwise> </choose> <!-- 是否蕴含本人 --> <choose> <when test="orgUnits.qcIncludeSelf == '1'.toString() "> or id=#{orgUnits.unitPid} ) </when> <otherwise> ) </otherwise> </choose> </if><if test="orgUnits.status!=null and orgUnits.status != '' "> and status = #{orgUnits.status} <!-- 失效状态卡失效、生效工夫 --> <if test="orgUnits.status == '02valid'.toString()"> <choose> <when test="orgUnits.qcDate!=null and orgUnits.qcDate !='' "> and (Date(#{orgUnits.qcDate}) between unit_sdate and unit_edate) </when> <otherwise> and (now() between unit_sdate and unit_edate) </otherwise> </choose> </if> </if> </if> </select> <!-- 组织架构图非凡解决mapper --> <resultMap id="chartResultMap" type="xxxx.xxx.xxx.xxx.xx.OrgUnitsChart"> <id column="id" jdbcType="BIGINT" property="unitId"/> <result column="unit_fname_cn" jdbcType="VARCHAR" property="label"/> <result column="unit_empid" jdbcType="VARCHAR" property="unitEmpid"/> </resultMap> <select id="selectChildren" resultMap="chartResultMap" parameterType="java.lang.Integer"> select id,unit_fname_cn,unit_empid from org_units where delete_flag = 0 and unit_update = 0 and status = '02valid' and unit_ischart = 1 and (now() between unit_sdate and unit_edate) and unit_pid = #{unitPid} </select> <select id="selectById" resultMap="chartResultMap" parameterType="java.lang.Integer"> select id,unit_fname_cn,unit_empid from org_units where delete_flag = 0 and unit_update = 0 and status = '02valid' and unit_ischart = 1 and (now() between unit_sdate and unit_edate) and id = #{id} </select> <select id="selectByPId" resultMap="BaseResultMap"> select <include refid="BaseColumnList" /> from org_units <where> delete_flag = 0 and unit_update = 0 <!-- and (date_format(sdate,'%Y-%m') &lt;= date_format(now(),'%Y-%m') and date_format(edate,'%Y-%m') &gt;= date_format(now(),'%Y-%m')) --> and status = '02valid' and (now() between unit_sdate and unit_edate) and unit_pid =#{pid} </where> </select></mapper><?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="xxx.xxx.xxx.xx.dao.OrgUnitsMapper"> <resultMap id="BaseResultMap" type="xxx.xxx.xxx.xx.OrgUnits"> </resultMap> <sql id="BaseColumnList"> </sql> <select id="selectChildren" resultMap="chartResultMap" parameterType="java.lang.Integer"> </select> </mapper>动静SQL标签<if test>标签<if>标签:是依据test属性中的布尔表达式的值,从而决定是否执行蕴含在其中的SQL片段。如果判断后果为true,则执行其中的SQL片段;如果后果为false,则不执行其中的SQL片段<where>标签<where>标签:用于对蕴含在其中的SQL片段进行检索,在须要时能够生成where关键字,并且在须要时会剔除多余的连接词(比方and或者or)<choose when otherwise>标签遇到choose标签判断when外面的条件,条件满足执行外面的sql,不满足持续判断下一个when标签的外面的条件,满足执行sql,不满足持续向下判断,都不满足执行otherwise标签外面的sql,相似switch,case,default。<foreach>标签foreach标签:能够对传过来的参数数组或汇合进行遍历,以下是foreach标签上的各个属性介绍:1.item:必须,若collection为数组或List汇合时,item示意其中的元素,若collection为map中的key,item示意map中value(汇合或数组)中的元素2.open:可选,示意遍历生成的SQL片段以什么开始,最罕用的是左括号'('3.collection:必须,值为遍历的汇合类型,例如:如果参数只是一个数组或List汇合,则collection的值为array或list;如果传的是多个参数,用map封装,collection则指定为map中的key。4.close:可选,示意遍历生成的SQL片段以什么完结,最罕用的是右括号')'5.separator:可选,每次遍历后给生成的SQL片段前面指定距离符6.index:以后迭代的序号<include>标签抽取公共sql片段,防止反复

February 8, 2021 · 8 min · jiezi

关于mybatis:学习笔记-MyBatis关于MyBatis

摘自:http://www.mybatis.cn/archive... MyBatisMyBatis是一个优良的长久层框架,MyBatis的根本工作原理就是:先封装SQL,接着调用JDBC操作数据库,最初把数据库返回的表后果封装成Java类。mybatis应用程序通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件中构建出SqlSessionFactory,而后,SqlSessionFactory的实例间接开启一个SqlSession,再通过SqlSession实例取得Mapper对象并运行Mapper映射的SQL语句,实现对数据库的CRUD和事务提交,之后敞开SqlSession。如下图所示 (1)读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连贯信息。(2)加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,须要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件能够加载多个映射文件,每个文件对应数据库中的一张表。(3)结构会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。(4)创立会话对象。由会话工厂创立SqlSession对象,该对象中蕴含了执行SQL语句的所有办法。(5)Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将依据SqlSession传递的参数动静地生成须要执行的SQL语句,同时负责查问缓存的保护。(6)MappedStatement对象。在Executor接口的执行办法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。(7)输出参数映射。输出参数类型能够是Map、List等汇合类型,也能够是根本数据类型和POJO类型。输出参数映射过程相似于JDBC对preparedStatement对象设置参数的过程。(8)输入后果映射。输入后果类型能够是Map、List等汇合类型,也能够是根本数据类型和POJO类型。输入后果映射过程相似于JDBC对后果集的解析过程。JDBCJDBC四大外围对象(1)DriverManager,用于注册数据库连贯(2)Connection,与数据库连贯对象(3)Statement/PrepareStatement,操作数据库SQL语句的对象(4)ResultSet,后果集或一张虚构表package Ring1270.pra.JDBC.Test;import java.sql.*;public class JDBCTest { public static void main(String[] args) throws Exception { Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; try { // Registration driver Class.forName("com.mysql.cj.jdbc.Driver"); // Get connection connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/jt_db?serverTimezone=GMT%2B8", "root", "1111" ); // Get transmitter // Sent sql String sql = "select * from account"; statement = connection.prepareStatement(sql); resultSet = statement.executeQuery(); // Deal rusult while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); double money = resultSet.getDouble("money"); System.out.println(id + ":" + name + ":" + money); } }catch (Exception e) { e.printStackTrace(); }finally { // Release connection if (resultSet != null) { try { resultSet.close(); }catch (Exception e) { e.printStackTrace(); }finally { resultSet = null; } } if (statement != null) { try { statement.close(); }catch (Exception e) { e.printStackTrace(); }finally { statement = null; } } if (connection != null) { try { connection.close(); }catch (Exception e) { e.printStackTrace(); }finally { connection= null; } } } }}MyBatisMyBatis四大外围对象:(1)SqlSession对象,该对象中蕴含了执行SQL语句的所有办法【1】。相似于JDBC外面的Connection 【2】。(2)Executor接口,它将依据SqlSession传递的参数动静地生成须要执行的SQL语句,同时负责查问缓存的保护。相似于JDBC外面的Statement/PrepareStatement。(3)MappedStatement对象,该对象是对映射SQL的封装,用于存储要映射的SQL语句的id、参数等信息。(4)ResultHandler对象,用于对返回的后果进行解决,最终失去本人想要的数据格式或类型。能够自定义返回类型。package Ring1270.pra.MyBatis;import Ring1270.pra.MyBatis.pojo.Emp;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.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class MyBatisTest { /** 练习1(疾速入门): 查问emp表中的所有员工, 返回一个List<Emp>汇合 * @throws IOException */ @Test public void findAll() throws IOException { //1.读取mybatis的外围配置文件(mybatis-config.xml) InputStream in = Resources .getResourceAsStream("mybatis-config.xml"); //2.通过配置信息获取一个SqlSessionFactory工厂对象 SqlSessionFactory fac = new SqlSessionFactoryBuilder().build( in ); //3.通过工厂获取一个SqlSession对象 SqlSession session = fac.openSession(); //4.通过namespace+id找到要执行的sql语句并执行sql语句 List<Emp> list = session .selectList("EmpMapper.findAll"); //5.输入后果 for(Emp e : list) { System.out.println( e ); } }}

February 5, 2021 · 2 min · jiezi

关于mybatis:强大MyBatis-三种流式查询方法

基本概念 流式查问指的是查问胜利后不是返回一个汇合而是返回一个迭代器,利用每次从迭代器取一条查问后果。流式查问的益处是可能升高内存应用。 如果没有流式查问,咱们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查问,而分页查问效率取决于表设计,如果设计的不好,就无奈执行高效的分页查问。因而流式查问是一个数据库拜访框架必须具备的性能。 流式查问的过程当中,数据库连贯是放弃关上状态的,因而要留神的是:执行一个流式查问后,数据库拜访框架就不负责敞开数据库连贯了,须要利用在取完数据后本人敞开。 MyBatis 流式查问接口 提供了一个叫 org.apache.ibatis.cursor.Cursor 的接口类用于流式查问,这个接口继承了 java.io.Closeable 和 java.lang.Iterable 接口,由此可知: Cursor 是可敞开的; Cursor 是可遍历的。 除此之外,Cursor 还提供了三个办法: isOpen():用于在取数据之前判断 Cursor 对象是否是关上状态。只有当关上时 Cursor 能力取数据; isConsumed():用于判断查问后果是否全副取完。 getCurrentIndex():返回曾经获取了多少条数据 因为 Cursor 实现了迭代器接口,因而在理论应用当中,从 Cursor 取数据非常简单: cursor.forEach(rowObject -> {...}); 但构建 Cursor 的过程不简略 咱们举个理论例子。上面是一个 Mapper 类: @Mapper public interface FooMapper { @Select("select * from foo limit #{limit}") Cursor<Foo> scan(@Param("limit") int limit); } 办法 scan() 是一个非常简单的查问。通过指定 Mapper 办法的返回值为 Cursor 类型,MyBatis 就晓得这个查询方法一个流式查问。 而后咱们再写一个 SpringMVC Controller 办法来调用 Mapper(无关的代码曾经省略): ...

January 31, 2021 · 2 min · jiezi

关于mybatis:Mybatis17-Mybatis自关联查询一对多查询

注:代码已托管在GitHub上,地址是:https://github.com/Damaer/Mybatis-Learning ,我的项目是mybatis-13-oneself-one2many,须要自取,须要配置maven环境以及mysql环境(sql语句在resource下的test.sql中),感觉有用能够点个小星星。 docsify文档地址在:https://damaer.github.io/Mybatis-Learning/#/ 所谓自关联查问,是指本人既然充当一方,又充当多方。比方新闻栏目的数据表,本人能够是父栏目,也能够是多方,子栏目。在数据表外面实现就是一张表,有一个外键pid,用来示意该栏目的父栏目,一级栏目没有父栏目的,能够将其外键设置为0。 DB表如下: 查问指定栏目的所有子孙栏目查问指定目录的所有子孙目录,咱们须要应用递归的思维,查出以后栏目之后,须要将以后栏目的id作为下一级栏目的pid。 实体类NewsLabel.java,应用一对多的关系: import java.util.Set;public class NewsLabel { private Integer id; private String name; private Set<NewsLabel>children; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<NewsLabel> getChildren() { return children; } public void setChildren(Set<NewsLabel> children) { this.children = children; } @Override public String toString() { return "NewsLabel [id=" + id + ", name=" + name + ", children=" + children + "]"; } }定义sql接口: ...

January 31, 2021 · 4 min · jiezi

关于mybatis:Mybatis16-Mybatis多对一关联查询

注:代码已托管在GitHub上,地址是:https://github.com/Damaer/Mybatis-Learning ,我的项目是mybatis-12-many2one,须要自取,须要配置maven环境以及mysql环境(sql语句在resource下的test.sql中),感觉有用能够点个小星星。 docsify文档地址在:https://damaer.github.io/Mybatis-Learning/#/ 一对多关联查问:每个国家有很多大臣,领导人,当初咱们查问一个领导,心愿能将他所在国家的信息连带进去,这就是一对多关联查问。 数据表如下:国家表: 领导人表: 创立数据库/表#创立数据库CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;#创立数据表CREATE TABLE `test`.`country` ( `cid` INT(10) NOT NULL AUTO_INCREMENT ,`cname` VARCHAR(20) NOT NULL ,PRIMARY KEY(`cid`)) ENGINE = MyISAM;CREATE TABLE `test`.`minister` ( `mid` INT(10) NOT NULL AUTO_INCREMENT ,`mname` VARCHAR(20) NOT NULL ,`countryId` INT(20) NOT NULL ,PRIMARY KEY(`mid`)) ENGINE = MyISAM;#初始化数据表INSERT INTO `country` (`cid`, `cname`) VALUES ('1', 'USA');INSERT INTO `country` (`cid`, `cname`) VALUES ('2', 'England');INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('1', 'aaa', '1');INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('2', 'bbb', '1');INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('3', 'ccc', '2');INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('4', 'ddd', '2');INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('5', 'eee', '2');实体类country类: ...

January 30, 2021 · 3 min · jiezi

关于mybatis:Mybatis15-Mybatis一对一多表关联查询

注:代码已托管在GitHub上,地址是:https://github.com/Damaer/Mybatis-Learning ,我的项目是mybatis-11-one2one,须要自取,须要配置maven环境以及mysql环境(sql语句在resource下的test.sql中),感觉有用能够点个小星星。 docsify文档地址在:https://damaer.github.io/Mybatis-Learning/#/ 所谓一对一多表查问,举个例子:咱们有很多国家,每一个国家只有一个领导人(假如),咱们须要依据id查问国家信息,带上领导人的信息。 创立数据表设计表的时候,咱们须要思考因为是一对多关系,咱们须要在国家表外面应用一个字段对应领导人的信息。 #创立数据库CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;#创立数据表CREATE TABLE `test`.`country` ( `cid` INT(10) NOT NULL AUTO_INCREMENT ,`cname` VARCHAR(20) NOT NULL ,`mid` INT(10) NOT NULL,PRIMARY KEY(`cid`)) ENGINE = MyISAM;CREATE TABLE `test`.`minister` ( `mid` INT(10) NOT NULL AUTO_INCREMENT ,`mname` VARCHAR(20) NOT NULL,PRIMARY KEY(`mid`)) ENGINE = MyISAM;#初始化数据表INSERT INTO `country` (`cid`, `cname`, `mid`) VALUES ('1', 'aaa', '1'); INSERT INTO `country` (`cid`, `cname`, `mid`) VALUES ('2', 'bbb', '2');INSERT INTO `country` (`cid`, `cname`, `mid`) VALUES ('3', 'ccc', '3'); INSERT INTO `minister` (`mid`, `mname`) VALUES ('1', 'sam'); INSERT INTO `minister` (`mid`, `mname`) VALUES ('2', 'jane'); INSERT INTO `minister` (`mid`, `mname`) VALUES ('3', 'jone'); 数据表如下: ...

January 26, 2021 · 3 min · jiezi

关于mybatis:打印个SQL还不老实

打印个SQL还不诚实摘要Mybatis开启sql打印,还不把参数给拼接到sql语句中,给本地开发带来极大不不便Mybatis-Plus是通过收集日志实现sql语句与参数联合,有些IDEA对这个插件还反对的不好不如本人搞个小工具,只有是本地形式启动的,将在控制台输入所有的查问和更新sql语句,并监听sql执行工夫小工具来了注入@Configurationpublic class MybatisConfig { @Bean @Conditional(LocalStartCondition.class) public SqlCheckInterceptor performanceInterceptor() { return new SqlCheckInterceptor(); }}注入条件public class LocalStartCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, @Nullable AnnotatedTypeMetadata annotatedTypeMetadata) { return "local".equals(conditionContext.getEnvironment().getActiveProfiles()[0]); }}拦截器@Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),})public class SqlCheckInterceptor implements Interceptor { private static final Log logger = LogFactory.getLog(SqlCheckInterceptor.class); private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; Object parameterObject = null; if (invocation.getArgs().length > 1) { parameterObject = invocation.getArgs()[1]; } long start = SystemClock.now(); Object result = invocation.proceed(); long timing = SystemClock.now() - start; String statementId = mappedStatement.getId(); BoundSql boundSql = mappedStatement.getBoundSql(parameterObject); Configuration configuration = mappedStatement.getConfiguration(); String sql = getSql(boundSql, parameterObject, configuration); if (logger.isInfoEnabled()) { String formatSqlStr = StringPool.NEWLINE + "SQL-in-method===>:" + statementId + StringPool.NEWLINE + "Execute-time====>:" + timing + " ms" + StringPool.NEWLINE + "SQL-script===>:" + StringPool.NEWLINE + sql + StringPool.NEWLINE; logger.info(formatSqlStr); } return result; } @Override public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } return target; } @Override public void setProperties(Properties properties) { } private String getSql(BoundSql boundSql, Object parameterObject, Configuration configuration) { String sql = boundSql.getSql().replaceAll("[\\s]+", " "); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); if (parameterMappings != null) { for (ParameterMapping parameterMapping : parameterMappings) { if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } sql = replacePlaceholder(sql, value); } } } return sql; } private String replacePlaceholder(String sql, Object propertyValue) { String result; if (propertyValue != null) { if (propertyValue instanceof String) { result = "'" + propertyValue + "'"; } else if (propertyValue instanceof Date) { result = "'" + DATE_FORMAT.format(propertyValue) + "'"; } else { result = propertyValue.toString(); } } else { result = "null"; } return sql.replaceFirst("\\?", Matcher.quoteReplacement(result)); }}废话**粘贴代码的同时想想实现原理,对晋升魂力有帮忙。**

January 7, 2021 · 2 min · jiezi

关于mybatis:mybatis使用及SQL语句优化小结

【摘要】 MyBatis 作为一款优良的长久层框架,它反对自定义SQL、存储过程以及高级映射。一、mybatis总结1.mybatis查问/更新语句,没有找到符合条件的记录,会返回什么?返回类型为String的, 理论返回null; 返回类型为对象的:理论返回null ; 返回类型为列表等汇合: 理论返回空集合[]; 返回类型为Boolean:理论返回false 当数据库语句插入条件不满足,会返回false;比方应用dual的SQL语句; 2.mybatis会抛出哪些常见异样?不是所有的异样,都认为程序出异样要报错, 比方用户反复珍藏商品,能够间接返回胜利,这时候对“违反惟一键”异样要非凡解决。 (1) 插入语句 DataIntegrityViolationException: 违反非空束缚、数据大小超过束缚 DuplicateKeyException:违反惟一键束缚 CannotAcquireLockException: for update nowait 超时 (2)更新语句 条件不满足时,会返回false 数据库操作应该判断返回值,比方上面BUG: (3) bug类,表字段找不到等场景 MyBatisSystemException BadSqlGrammarException 3.事务外面套用for update,看上去绕过了事务等for update获取锁后,select进去的是最新的数据  4.for update是一种行级锁,又叫排它锁一旦用户对某个行施加了行级加锁,则该用户能够查问也能够更新被加锁的数据行,其它用户只能查问但不能更新被加锁的数据行; 如果查问条件带有主键,会锁行数据,如果没有,会锁表。 如果肯定要用FOR UPDATE,倡议加上NOWAIT 或  for update wait 3 二、SQL优化约束条件:数据表减少表的约束条件,避免脏数据。2. limit1:如果咱们晓得返回后果只有 1 条,就能够应用LIMIT 1,通知 SELECT 语句只须要返回一条记录即可。这样的益处就是 SELECT 不须要扫描残缺的表,只须要检索到一条符合条件的记录即可返回。 3.拼写格调:SQL保留字应用英文大写,其余应用英文小写;进步可读性。 4.Like:应用like加通配符,可能使得索引生效,会触发全表扫描。如果要让索引失效,那么 LIKE 前面就不能以(%)结尾,比方应用LIKE '%太%'或LIKE '%太'的时候就会对全表进行扫描。如果应用LIKE '太%',同时检索的字段进行了索引的时候,则不会进行全表扫描。  5.对罕用于搜寻的字段增加索引,能极大减少查问效率  6.应用“自连贯”优于子查问。  7.应用视图: 视图能够了解成给一个查问SQL起个别名。 只不过提前通过编译,视图不能传入变量,不保留数据 视图的长处是:断绝数据表操作  8.应用长期表:  9.IN/EXIST 应用相似两层for循环,遵循小表驱动大表准则。 如果两个表中一个较小,一个是大表,则子查问表大的用exists,子查问表小的用in:例如:表A(小表),表B(大表) select * from A where cc in (select cc from B) ;// 效率低,用到了A表上cc列的索引; select * from A where exists(select cc from B where cc=A.cc) ;// 效率高,用到了B表上cc列的索引。  10.不倡议应用索引的状况: (1)总数据量很少。 (2)数据反复度大,且不同取值散布平均,比方性别男女比例各靠近50%。 11.惟一索引/惟一键有3个字段时,按其中1个字段查问,是否比没有索引效率高? ---- 没有,这种状况创立惟一键,更多的是为了保证数据正确性。 ...

January 6, 2021 · 1 min · jiezi

关于mybatis:一文彻底吃透MyBatis源码

写在后面随着互联网的倒退,越来越多的公司摒弃了Hibernate,而抉择拥抱了MyBatis。而且,很多大厂在面试的时候喜爱问MyBatis底层的原理和源码实现。总之,MyBatis简直成为了Java开发人员必须深刻把握的框架技术,明天,咱们就一起来深入分析MyBatis源码。文章有点长,倡议先珍藏后缓缓钻研。整体三万字左右,全程高能,小伙伴们可缓缓钻研。 文章已收录到: https://github.com/sunshinelyz/technology-binghe https://gitee.com/binghe001/technology-binghe MyBatis源码解析大家应该都晓得Mybatis源码也是对Jbdc的再一次封装,不管怎么进行包装,还是会有获取链接、preparedStatement、封装参数、执行这些步骤的。 配置解析过程String resource = "mybatis-config.xml";//1.读取resources上面的mybatis-config.xml文件InputStream inputStream = Resources.getResourceAsStream(resource);//2.应用SqlSessionFactoryBuilder创立SqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//3.通过sqlSessionFactory创立SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();Resources.getResourceAsStream(resource)读取文件public static InputStream getResourceAsStream(String resource) throws IOException { return getResourceAsStream(null, resource);} //loader赋值为nullpublic static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); if (in == null) { throw new IOException("Could not find resource " + resource); } return in;}//classLoader为nullpublic InputStream getResourceAsStream(String resource, ClassLoader classLoader) { return getResourceAsStream(resource, getClassLoaders(classLoader));} //classLoader类加载InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { for (ClassLoader cl : classLoader) { if (null != cl) { //加载指定门路文件流 InputStream returnValue = cl.getResourceAsStream(resource); // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource if (null == returnValue) { returnValue = cl.getResourceAsStream("/" + resource); } if (null != returnValue) { return returnValue; } } } return null;}总结:次要是通过ClassLoader.getResourceAsStream()办法获取指定的classpath门路下的Resource 。 ...

January 5, 2021 · 10 min · jiezi

关于mybatis:面试官MyBatis的SQL执行流程说这么清楚网上抄的吧

前言MyBatis可能很多人都始终在用,然而MyBatis的SQL执行流程可能并不是所有人都分明了,那么既然进来了,通读本文你将播种如下: 1、Mapper接口和映射文件是如何进行绑定的2、MyBatis中SQL语句的执行流程3、自定义MyBatis中的参数设置处理器typeHandler4、自定义MyBatis中后果集处理器typeHandlerPS:本文基于MyBatis3.5.5版本源码 概要在MyBatis中,利用编程式进行数据查问,次要就是上面几行代码: SqlSession session = sqlSessionFactory.openSession();UserMapper userMapper = session.getMapper(UserMapper.class);List<LwUser> userList = userMapper.listUserByUserName("孤狼1号");123第一行是获取一个SqlSession对象在上一篇文章剖析过了,想要具体理解的能够点击这里,第二行就是获取UserMapper接口,第三行一行代码就实现了整个查问语句的流程,接下来咱们就来仔细分析一下第二和第三步。 获取Mapper接口(getMapper)第二步是通过SqlSession对象是获取一个Mapper接口,这个流程还是绝对简略的,上面就是咱们调用session.getMapper办法之后的运行时序图: 1、在调用getMapper之后,会去Configuration对象中获取Mapper对象,因为在我的项目启动的时候就会把Mapper接口加载并解析存储到Configuration对象 2、通过Configuration对象中的MapperRegistry对象属性,持续调用getMapper办法3、依据type类型,从MapperRegistry对象中的knownMappers获取到以后类型对应的代理工厂类,而后通过代理工厂类生成对应Mapper的代理类4、最终获取到咱们接口对应的代理类MapperProxy对象而MapperProxy能够看到实现了InvocationHandler,应用的就是JDK动静代理。至此获取Mapper流程完结了,那么就有一个问题了MapperRegistry对象内的HashMap属性knownMappers中的数据是什么时候存进去的呢?Mapper接口和映射文件是何时关联的Mapper接口及其映射文件是在加载mybatis-config配置文件的时候存储进去的,上面就是时序图: 1、首先咱们会手动调用SqlSessionFactoryBuilder办法中的build()办法:2、而后会结构一个XMLConfigBuilder对象,并调用其parse办法:3、而后会持续调用本人的parseConfiguration来解析配置文件,这外面就会别离去解析全局配置文件的顶级节点,其余的咱们先不看,咱们间接看最初解析mappers节点4、持续调用本人的mapperElement来解析mappers文件(这个办法比拟长,为了不便截图残缺,所以把字体放大了1号),能够看到,这外面分了四种形式来解析mappers节点的配置,对应了4种mapper配置形式,而其中红框内的两种形式是间接配置的xml映射文件,蓝框内的两种形式是解析间接配置Mapper接口的形式,从这里也能够阐明,不管配置哪种形式,最终MyBatis都会将xml映射文件和Mapper接口进行关联。5、咱们先看第2种和第3中(间接配置xml映射文件的解析形式),会构建一个XMLMapperBuilder对象并调用其parse办法。然而这里有一个问题,如果有多重继承或者多重依赖时在这里是可能会无奈被齐全解析的,比如说三个映射文件相互依赖,那么if外面(假如是最坏状况)只能加载1个,失败2个,而后走到上面if之外的代码又只能加载1个,还有1个会失败(如下代码中,只会解决1次,再次失败并不会持续退出incompleteResultMaps):当然,这个还是会被解析的,前面执行查问的时候会再次通过一直遍历去全副解析结束,不过有一点须要留神的是,相互援用这种是会导致解析失败报错的,所以在开发过程中咱们应该防止循环依赖的产生。6、解析完映射文件之后,调用本身办法bindMapperForNamespace,开始绑定Mapper接口和映射文件:7、调用Configuration对象的addMapper8、调用Configuration对象的属性MapperRegistry内的addMapper办法,这个办法就是正式将Mapper接口增加到knownMappers,所以下面getMapper能够间接获取:到这里咱们就实现了Mapper接口和xml映射文件的绑定9、留神下面红框外面的代码,又调用了一次parse办法,这个parse办法次要是解析注解,比方上面的语句:@Select("select * from lw_user") List<LwUser> listAllUser();12所以这个办法外面会去解析@Select等注解,须要留神的是,parse办法外面会同时再解析一次xml映射文件,因为下面咱们提到了mappers节点有4种配置形式,其中两种配置的是Mapper接口,而配置Mapper接口会间接先调用addMapper接口,并没有解析映射文件,所以进入注解解析办法parse之中会须要再尝试解析一次XML映射文件。解析实现之后,还会对Mapper接口中的办法进行解析,并将每个办法的全限定类名作为key存入存入Configuration中的mappedStatements属性。 须要指出的是,这里存储的时候,同一个value会存储2次,一个全限定名作为key,另一个就是只用办法名(sql语句的id)来作为key:所以最终mappedStatements会是上面的状况:事实上如果咱们通过接口的形式来编程的话,最初来getStatement的时候,都是依据全限定名来取的,所以即便有重名对咱们也没有影响,而之所以要这么做的起因其实还是为了兼容晚期版本的用法,那就是不通过接口,而是间接通过办法名的形式来进行查问: session.selectList("com.lonelyWolf.mybatis.mapper.UserMapper.listAllUser");1这里如果shortName没有反复的话,是能够间接通过简写来查问的: session.selectList("listAllUser");1然而通过简写来查问一旦shortName反复了就会抛出以下异样:这里的异样其实就是StrickMap的get办法抛出来的: sql执行流程剖析下面咱们讲到了,获取到的Mapper接口实际上被包装成为了代理对象,所以咱们执行查问语句必定是执行的代理对象办法,接下来咱们就以Mapper接口的代理对象MapperProxy来剖析一下查问流程。 整个sql执行流程能够分为两大步骤: 一、寻找sql二、执行sql语句寻找sql首先还是来看一下寻找sql语句的时序图: 1、理解代理模式的应该都晓得,调用被代理对象的办法之后实际上执行的就是代理对象的invoke办法2、因为咱们这里并没有调用Object类中的办法,所以必定走的else。else中会持续调用MapperProxy外部类MapperMethodInvoker中的办法cachedInvoker,这外面会有一个判断,判断一下咱们是不是default办法,因为Jdk1.8中接口中能够新增default办法,而default办法是并不是一个形象办法,所以也须要非凡解决(刚开始会从缓存外面取,缓存相干常识咱们这里先不讲,前面会独自写一篇来剖析一下缓存))。3、接下来,是结构一个MapperMethod对象,这个对象封装了Mapper接口中对应的办法信息以及对应的sql语句信息:这外面就会把要执行的sql语句,申请参数,办法返回值全副解析封装成MapperMethod对象,而后前面就能够开始筹备执行sql语句了执行sql语句还是先来看一下执行Sql语句的时序图:1、咱们持续下面的流程进入execute办法: 2、这外面会依据语句类型以及返回值类型来决定如何执行,自己这里返回的是一个汇合,故而咱们进入executeForMany办法:3、这外面首先会将后面存好的参数进行一次转换,而后绕了这么一圈,回到了终点SqlSession对象,持续调用selectList办法:3、接下来又讲流程委派给了Execute去执行query办法,最终又会去调用queryFromDatabase办法:4、到这里之后,终于要进入正题了,个别带了这种do结尾的办法就是真正做事的,Spring中很多中央也是采纳的这种命名形式:留神,后面咱们的sql语句还是占位符的形式,并没有将参数设置进去,所以这里在return下面一行调用prepareStatement办法创立Statement对象的时候会去设置参数,替换占位符。参数如何设置咱们先跳过,等把流程执行完了咱们在独自剖析参数映射和后果集映射。5、持续进入PreparedStatementHandler对象的query办法,能够看到,这一步就是调用了jdbc操作对象PreparedStatement中的execute办法,最初一步就是转换后果集而后返回。到这里,整个SQL语句执行流程剖析就完结了,中途有一些参数的存储以及转换并没有深刻进去,因为参数的转换并不是外围,只有分明整个数据的流转流程,咱们本人也能够有本人的实现形式,只有存起来最初咱们能从新解析读出来就行。 参数映射当初咱们来看一下下面在执行查问之前参数是如何进行设置的,咱们先进入prepareStatement办法:咱们发现,最终是调用了StatementHandler中的parameterize进行参数设置,接下来这里为了节俭篇幅,咱们不会一步步点进去,间接进入设置参数的办法:下面的BaseTypeHandler是一个抽象类,setNonNullParameter并没有实现,都是交给子类去实现,而每一个子类就是对应了数据库的一种类型。下图中就是默认的一个子类StringTypeHandler,外面没什么其余逻辑,就是设置参数。能够看到String外面调用了jdbc中的setString办法,而如果是int也会调用setInt办法。看到这些子类如果大家之前浏览过我后面讲的MyBatis参数配置,应该就很显著能够晓得,这些子类就是零碎默认提供的一些typeHandler。而这些默认的typeHandler会默认被注册并和Java对象进行绑定:正是因为MyBatis中默认提供了罕用数据类型的映射,所以咱们写Sql的时候才能够省略参数映射关系,能够间接采纳上面的形式,零碎能够依据咱们参数的类型,主动抉择适合的typeHander进行映射: select user_id,user_name from lw_user where user_name=#{userName}1下面这条语句实际上和上面这条是等价的: select user_id,user_name from lw_user where user_name=#{userName,jdbcType=VARCHAR}1或者说咱们能够间接指定typeHandler: select user_id,user_name from lw_user where user_name=#{userName,jdbcType=VARCHAR,typeHandler=org.apache.ibatis.type.IntegerTypeHandler}1这里因为咱们配置了typeHandler,所以会优先以配置的typeHandler为主不会再去读取默认的映射,如果类型不匹配就会间接报错了:看到这里很多人应该就晓得了,如果咱们本人自定义一个typeHandler,而后就能够配置成咱们本人的自定义类。所以接下来就让咱们看看如何自定义一个typeHandler 自定义typeHandler自定义typeHandler须要实现BaseTypeHandler接口,BaseTypeHandler有4个办法,包含后果集映射,为了节俭篇幅,代码没有写上来: package com.lonelyWolf.mybatis.typeHandler;import org.apache.ibatis.type.BaseTypeHandler;import org.apache.ibatis.type.JdbcType;import java.sql.CallableStatement;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;public class MyTypeHandler extends BaseTypeHandler<String> { @Override public void setNonNullParameter(PreparedStatement preparedStatement, int index, String param, JdbcType jdbcType) throws SQLException { System.out.println("自定义typeHandler失效了"); preparedStatement.setString(index,param); }1234567891011121314151617而后咱们改写一下下面的查问语句: ...

December 31, 2020 · 1 min · jiezi

关于mybatis:mybatis-配置详解

mybatis-config.xml 外围配置文件mybatis-config.xml 蕴含的内容如下 configuration(配置)properties(属性)settings(设置)typeAliases(类型别名)typeHandlers(类型处理器)objectFactory(对象工厂)plugins(插件)environments(环境配置)environment(环境变量)transactionManager(事务管理器)dataSource(数据源)databaseIdProvider(数据库厂商标识)mappers(映射器)留神元素节点的程序!程序不对会报错 1. environments元素<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> <environment id="test"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment></environments>environments配置mybatis 多套环境,将sql 映射到多个不同的数据库上,必须指定一个默认环境,即default="development"1.1 子元素environment其中dataSource 数据源(共三种内建的数据源类型) type="[UNPOOLED|POOLED|JNDI]")unpooled:这个数据源的实现只是每次被申请时关上和敞开连贯pooled:这种数据源的实现利用“池”的概念将 JDBC 连贯对象组织起来 , 这是一种使得并发 Web 利用疾速响应申请的风行解决形式。jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中应用,容器能够集中或在内部配置数据源,而后搁置一个 JNDI 上下文的援用。注: 数据源也有很多第三方的实现,比方dbcp,c3p0,druid等等.... 1.2 transactionManager 事务管理器(共两种) <transactionManager type="[ JDBC | MANAGED ]"/>2. mappers 元素(定义映射SQL语句文件)次要用于找到sql语句的文件在哪里?能够应用不同的形式援用sql语句 具体的援用形式如下 应用相对路径引入sql语句的文件<!-- 应用绝对于类门路的资源援用 --><mappers> <mapper resource="org/mybatis/builder/PostMapper.xml"/></mappers>应用齐全限定资源定位符(URL)<!-- 应用齐全限定资源定位符(URL) --><mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/></mappers>应用映射器接口实现类的齐全限定类名,须要配置文件名称和接口名称统一,并且位于同一目录下<!--应用映射器接口实现类的齐全限定类名须要配置文件名称和接口名称统一,并且位于同一目录下--><mappers> <mapper class="org.mybatis.builder.AuthorMapper"/></mappers>将包内的映射器接口实现全副注册为映射器然而须要配置文件名称和接口名称统一,并且位于同一目录下<!--将包内的映射器接口实现全副注册为映射器.然而须要配置文件名称和接口名称统一,并且位于同一目录下--><mappers> <package name="org.mybatis.builder"/></mappers>**mapper配置文件次要用用关联dao接口中的办法,并书写sql语句 相当于实现了接口中的各个办法 ...

December 30, 2020 · 2 min · jiezi

关于mybatis:MyBatis-的执行流程写得太好了

MyBatis可能很多人都始终在用,然而MyBatis的SQL执行流程可能并不是所有人都分明了,那么既然进来了,通读本文你将播种如下: 1、Mapper接口和映射文件是如何进行绑定的2、MyBatis中SQL语句的执行流程3、自定义MyBatis中的参数设置处理器typeHandler4、自定义MyBatis中后果集处理器typeHandlerPS:本文基于MyBatis3.5.5版本源码。 概要在MyBatis中,利用编程式进行数据查问,次要就是上面几行代码: SqlSession session = sqlSessionFactory.openSession();UserMapper userMapper = session.getMapper(UserMapper.class);List<LwUser> userList = userMapper.listUserByUserName("孤狼1号"); 第一行是获取一个SqlSession对象在上一篇文章剖析过了,想要具体理解的能够点击这里,第二行就是获取UserMapper接口,第三行一行代码就实现了整个查问语句的流程,接下来咱们就来仔细分析一下第二和第三步。 获取Mapper接口(getMapper)第二步是通过SqlSession对象是获取一个Mapper接口,这个流程还是绝对简略的,上面就是咱们调用session.getMapper办法之后的运行时序图: 1、在调用getMapper之后,会去Configuration对象中获取Mapper对象,因为在我的项目启动的时候就会把Mapper接口加载并解析存储到Configuration对象 2、通过Configuration对象中的MapperRegistry对象属性,持续调用getMapper办法 3、依据type类型,从MapperRegistry对象中的knownMappers获取到以后类型对应的代理工厂类,而后通过代理工厂类生成对应Mapper的代理类 4、最终获取到咱们接口对应的代理类MapperProxy对象 而MapperProxy能够看到实现了InvocationHandler,应用的就是JDK动静代理。

December 30, 2020 · 1 min · jiezi

关于mybatis:Mybatis10-Mybatis属性名和查询字段名不同怎么做

很多时候咱们有这样的需要,数据库的字段名与实体类的属性名不统一,这个时候咱们须要怎么做呢?有两种解决方案,第一种:间接在查问的时候应用别名,将别名设置成与实体类的属性名统一。第二种:应用resultType,本人定义映射关系。整个我的项目的目录如下:首先,咱们须要搭建数据库mysql环境(test.sql),id咱们写成了sid,name咱们写成了sname,age咱们写成了sage: #创立数据库CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;#创立数据表CREATE TABLE `student` ( `sid` INT NOT NULL AUTO_INCREMENT , `sname` VARCHAR(20) NOT NULL ,`sage` INT NOT NULL , `score` DOUBLE NOT NULL , PRIMARY KEY (`sid`)) ENGINE = MyISAM;Student.class实体类: public class Student { private Integer id; private String name; private int age; private double score; public Student(){ } public Student(String name, int age, double score) { super(); this.name = name; this.age = age; this.score = score; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + ", score=" + score + "]"; } }pom.xml: ...

December 26, 2020 · 3 min · jiezi

关于mybatis:Mybatis7-Mybatis如何知道增删改是否成功执行

代码间接放在Github仓库【https://github.com/Damaer/Myb... 】 须要申明的是:此Mybatis学习笔记,是从原始的Mybatis开始的,而不是整合了其余框架(比方Spring)之后,集体认为,这样能对它的性能,它能帮咱们做什么,有更好的了解,前面再缓缓叠加其余的性能。咱们晓得很多时候咱们有一个需要,咱们须要把插入数据后的id返回来,以便咱们下一次操作。 其实一开始的思路是我插入之后,再执行一次select,依据一个惟一的字段来执行select操作,然而Student这个类如果插入后再依据名字或者年龄查出来,这根本就是不可行的!!!重名与同年龄的人肯定不少。咱们的测试方法如下,咱们能够看到插入前是没有值的,插入后就有了值: /** * 测试插入后获取id */@Testpublic void testinsertStudentCacheId(){ Student student=new Student("helloworld",17,85); System.out.println("插入前:student="+student); dao.insertStudentCacheId(student); System.out.println("插入后:student="+student);}useGeneratedKeys 设置主键自增 <insert id="insertStudentCacheId" useGeneratedKeys="true" keyProperty="id" parameterType="Student"> insert into student(name,age,score) values(#{name},#{age},#{score}) </insert>须要留神的点: 1.useGeneratedKeys="true"示意设置属性自增2.keyProperty="id"设置主键的字段3.parameterType="Student"设置传入的类型4.留神:尽管有返回类型,然而咱们不须要手动设置返回的类型,这个是由框架帮咱们实现的,所以对应的接口办法也是没有返回值的,会批改咱们插入的对象,设置id值。5.实体类中id属性字段肯定须要set以及get办法 应用selectKey 查问主键 <insert id="insertStudentCacheId" parameterType="Student"> insert into student(name,age,score) values(#{name},#{age},#{score}) <!-- 指定后果类型resultType,keyProperty是属性,主动返回到属性id中,order是秩序,after是指获取id是在于插入后 --> <selectKey resultType="int" keyProperty="id" order="AFTER"> select @@identity </selectKey> </insert>或者写成: <insert id="insertStudentCacheId" parameterType="Student"> insert into student(name,age,score) values(#{name},#{age},#{score}) <!-- 指定后果类型resultType,keyProperty是属性,主动返回到属性id中,order是秩序,after是指获取id是在于插入后 --> <selectKey resultType="int" keyProperty="id" order="AFTER"> select LAST_INSERT_ID() </selectKey> </insert>两种形式的后果:[](http://markdownpicture.oss-cn... 留神要点: 1.最外层的<insert></insert>没有返回属性(resultType),然而外面的<selectKey></selectKey>是有返回值类型的。2.order="AFTER"示意先执行插入,之后才执行selectkey语句的。3.select @@identity和select LAST_INSERT_ID()都示意选出刚刚插入的最初一条数据的id。4.实体类中id属性字段肯定须要set以及get办法5.此时,接口中仍不须要有返回值,框架会主动将值注入到咱们insert的那个对象中,咱们能够间接应用就能够了。其实,咱们的接口中能够有返回值,然而这个返回值不是id,而是示意插入后影响的行数,此时sql中仍和下面一样,不须要写返回值。 <insert id="insertStudentCacheIdNoReturn" parameterType="Student"> insert into student(name,age,score) values(#{name},#{age},#{score}) <!-- 指定后果类型resultType,keyProperty是属性,主动返回到属性id中,order是秩序,after是指获取id是在于插入后 --> <selectKey resultType="int" keyProperty="id" order="AFTER"> select LAST_INSERT_ID() </selectKey> </insert>接口中: ...

December 26, 2020 · 1 min · jiezi

关于mybatis:25道mybatis面试题一次性打包给你

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 1.JDBC有几个步骤?JDBC大抵能够分为六个步骤: 加载驱动程序取得数据库连贯创立一个Statement对象操作数据库,实现增删改查获取后果集敞开资源2.什么是Mybatis?如果在面试的时候被问到,只有你说出上面三种即可: ❝MyBatis 是一款优良的长久层框架,它反对自定义 SQL、存储过程以及高级映射。 MyBatis 罢黜了简直所有的 JDBC 代码以及设置参数和获取后果集的工作。 MyBatis 能够通过简略的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,一般老式 Java 对象)为数据库中的记录。 ❞ ——来自官网。举荐大家依照官网的来答复。 3.什么是ORM?全称为Object Relational Mapping。对象-映射-关系型数据库。对象关系映射(简称ORM,或O/RM,或O/R mapping),用于实现面向对象编程语言里不同类型零碎的数据之间的转换。简略的说,ORM是通过应用形容对象和数据库之间映射的元数据,将程序中的对象与关系数据库互相映射。 ORM提供了实现长久化层的另一种模式,它采纳映射元数据来形容对象关系的映射,使得ORM中间件能在任何一个利用的业务逻辑层和数据库层之间充当桥梁。 4.说说ORM的优缺点「长处」1.进步了开发效率。因为ORM能够主动对Entity对象与数据库中的Table进行字段与属性的映射,所以咱们理论可能曾经不须要一个专用的、宏大的数据拜访层。2.ORM提供了对数据库的映射,不必sql间接编码,可能像操作对象一样从数据库获取数据。 「毛病」就义程序的执行效率和会固定思维模式,升高了开发的灵活性。 从系统结构上来看,采纳ORM的零碎个别都是多层零碎,零碎的档次多了,效率就会升高。ORM是一种齐全的面向对象的做法,而面向对象的做法也会对性能产生肯定的影响。 在咱们开发零碎时,个别都有性能问题。性能问题次要产生在算法不正确和与数据库不正确的应用上。 ORM所生成的代码个别不太可能写出很高效的算法,在数据库利用上更有可能会被误用,次要体现在对长久对象的提取和和数据的加工解决上,如果用上了ORM,程序员很有可能将全副的数据提取到内存对象中,而后再进行过滤和加工解决,这样就容易产生性能问题。 在对对象做长久化时,ORM个别会长久化所有的属性,有时,这是不心愿的。但ORM是一种工具,工具的确能解决一些反复,简略的劳动。这是不可否认的。但咱们不能指望工具能一劳永逸的解决所有问题,有些问题还是须要非凡解决的,但须要非凡解决的局部对绝大多数的零碎,应该是很少的。 5.说说Mybaits的优缺点长处① 基于SQL语句编程,相当灵便,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于对立治理;提供XML标签,反对编写动静SQL语句,并可重用。 ② 与JDBC相比,缩小了50%以上的代码量,打消了JDBC大量冗余的代码,不须要手动开关连贯; ③ 很好的与各种数据库兼容(因为MyBatis应用JDBC来连贯数据库,所以只有JDBC反对的数据库MyBatis都反对)。 ④ 可能与Spring很好的集成; ⑤ 提供映射标签,反对对象与数据库的ORM字段关系映射;提供对象关系映射标签,反对对象关系组件保护。 毛病① SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有肯定要求。 ② SQL语句依赖于数据库,导致数据库移植性差,不能随便更换数据库。 MyBatis框架实用场合: (1)MyBatis专一于SQL自身,是一个足够灵便的DAO层解决方案。 (2)对性能的要求很高,或者需要变动较多的我的项目,如互联网我的项目,MyBatis将是不错的抉择。 6.Mybatis是如何进行分页的?Mybatis应用RowBounds对象进行分页,它是针对ResultSet后果集执行的内存分页,而非物理分页,先把数据都查出来,而后再做分页。 能够在sql内间接书写带有物理分页的参数来实现物理分页性能,也能够应用分页插件来实现物理分页。 7.分页插件的基本原理是什么?分页插件的基本原理是应用Mybatis提供的插件接口,实现自定义插件,在插件的拦挡办法内拦挡待执行的sql,而后重写sql(SQL拼接limit),依据dialect方言,增加对应的物理分页语句和物理分页参数,用到了技术JDK动静代理,用到了责任链设计模式。 8.简述Mybatis的插件运行原理?Mybatis仅能够编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis应用JDK的动静代理,为须要拦挡的接口生成代理对象以实现接口办法拦挡性能,每当执行这4种接口对象的办法时,就会进入拦挡办法,具体就是InvocationHandler的invoke()办法,当然,只会拦挡那些你指定须要拦挡的办法。 9.如何编写一个插件?编写插件:实现Mybatis的Interceptor接口并复写intercept()办法,而后再给插件编写注解,指定要拦挡哪一个接口的哪些办法即可,最初在配置文件中配置你编写的插件。举荐:倡议珍藏,mybatis插件原理详解 10.Mybatis动静sql有什么用?Mybatis动静sql能够在Xml映射文件内,以标签的模式编写动静sql,执行原理是依据表达式的值实现逻辑判断 并动静调整sql的性能。 Mybatis提供了9种动静sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。 ...

December 25, 2020 · 1 min · jiezi

关于mybatis:不明白mybatis一级缓存那我用这5个问题讲给你听

一级缓存次要内容: 一级缓存也叫本地缓存(LocalCache),Mybatis的一级缓存是会话级别(SqlSession)层面进行缓存的。Mybatis的一级缓存是默认开启的。咱们开发我的项目中不须要做任何配置,然而如果想敞开一级缓存,能够应用localCacheScopde=statement来敞开。 如何敞开一级缓存呢?在BaseExecutor的中,请看上面代码: 为什么说是SqlSession层面缓存?就是一级缓存的生命周期和一个SqlSession对象的生命周期一样。 上面这段中,就会应用到一级缓存。 `SqlSession sqlSession1 = sqlSessionFactory.openSession();User user1 = sqlSession1.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);User user2 = sqlSession1.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);` 后果输入: 用两张图来总结: 第一次:查数据库,放入到缓存中。 第二次:间接从缓存中获取。 上面这段代码中就应用不到缓存 `SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);sqlSession = sqlSessionFactory.openSession();sqlSession1 = sqlSessionFactory.openSession();    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);    System.out.println("第一次查问");System.out.println(userMapper.selectById(1));    User user = new User();user.setUserName("tian111");user.setId(1);userMapper1.updateAuthorIfNecessary(user);System.out.println("第二次查问");System.out.println(userMapper.selectById(1));` 输入后果: 用三张图来总结: 第一次查问:sqlSession1查询数据库,放入到缓存中。 更新:sqlSession2进行更新,留神这里写入的是sqlSession本人的本地缓存。 第二次查问:sqlSession1第二次查问。 记住是一级缓存只能是同一个SqlSession对象就行了。 一级缓存保护在哪里的?既然一级缓存的生命周期和SqlSession统一,那么咱们能够猜测,这个缓存是不是就保护在SqlSession中呢? SqlSession的默认实现类DefaultSqlSession,在DefaultSqlSession中只有两个属性可能寄存缓存: `private final Configuration configuration;private final Executor executor;` configuration是全局的,必定不会放缓存。 那就只能把心愿寄托于Executor了。因为Executor是个接口,咱们能够看看他的实现类: 另外这里有个BaseExecutor。有各类也得瞄瞄。一看竟然有货色。 `public abstract class BaseExecutor implements Executor {  private static final Log log = LogFactory.getLog(BaseExecutor.class);  protected Transaction transaction;  protected Executor wrapper;  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;  //相熟的家伙,根本缓存  protected PerpetualCache localCache;  protected PerpetualCache localOutputParameterCache;  protected Configuration configuration;      protected int queryStack;  private boolean closed;      protected BaseExecutor(Configuration configuration, Transaction transaction) {    this.transaction = transaction;    this.deferredLoads = new ConcurrentLinkedQueue<>();    this.localCache = new PerpetualCache("LocalCache");    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");    this.closed = false;    this.configuration = configuration;    this.wrapper = this;  }    }` 再看看BaseExecutor类图: 所以这就证实了,这个缓存是保护在SqlSession里。 一级缓存什么时候被清空?在执行update、insert、delete、flushCache="true"、commit、rollback、LocalCacheScope.STATEMENT等状况下,一级缓存就都会被清空。 `@Overridepublic void clearLocalCache() {    if (!closed) {      localCache.clear();      localOutputParameterCache.clear();    }}` update时,一级缓存会被清空。delete和insert都是调用这个update。能够从SqlSession的insert、update、delete办法跟踪。 LocalCacheScope.STATEMENT时,一级缓存会被清空。在BaseExecutor里的query办法中: 事务提交回滚时,一级缓存会被清空。 flushCache="true"时,一级缓存会被清空。 一级缓存key是什么? 上面就是一级缓存key的创立过程 `@Overridepublic CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {    if (closed) {      throw new ExecutorException("Executor was closed.");    }    CacheKey cacheKey = new CacheKey();    cacheKey.update(ms.getId());    cacheKey.update(rowBounds.getOffset());    cacheKey.update(rowBounds.getLimit());    cacheKey.update(boundSql.getSql());    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();    // mimic DefaultParameterHandler logic    for (ParameterMapping parameterMapping : parameterMappings) {      if (parameterMapping.getMode() != ParameterMode.OUT) {        Object value;        String propertyName = parameterMapping.getProperty();        if (boundSql.hasAdditionalParameter(propertyName)) {          value = boundSql.getAdditionalParameter(propertyName);        } else if (parameterObject == null) {          value = null;        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {        value = parameterObject;        } else {        MetaObject metaObject = configuration.newMetaObject(parameterObject);        value = metaObject.getValue(propertyName);        }        cacheKey.update(value);      }    }    if (configuration.getEnvironment() != null) {    // issue #176    cacheKey.update(configuration.getEnvironment().getId());    }    return cacheKey;  }` id:com.tian.mybatis.mapper.UserMapper.selectById key的生成策略:id + offset + limit + sql + param value + environment id,这些值都雷同,生成的key就雷同。 ...

December 19, 2020 · 1 min · jiezi

关于mybatis:建议收藏mybatis插件原理详解

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 上次发文说到了如何集成分页插件MyBatis插件原理剖析,看完感觉本人better了,明天咱们接着来聊mybatis插件的原理。 插件原理剖析mybatis插件波及到的几个类: 我将以 Executor 为例,剖析 MyBatis 是如何为 Executor 实例植入插件的。Executor 实例是在开启 SqlSession 时被创立的,因而,咱们从源头进行剖析。先来看一下 SqlSession 开启的过程。 public SqlSession openSession() {    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {    Transaction tx = null;    try {        // 省略局部逻辑                // 创立 Executor        final Executor executor = configuration.newExecutor(tx, execType);        return new DefaultSqlSession(configuration, executor, autoCommit);    }     catch (Exception e) {...}     finally {...}}Executor 的创立过程封装在 Configuration 中,咱们跟进去看看看。 // Configuration类中public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    executorType = executorType == null ? defaultExecutorType : executorType;    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;    Executor executor;        // 依据 executorType 创立相应的 Executor 实例    if (ExecutorType.BATCH == executorType) {...}     else if (ExecutorType.REUSE == executorType) {...}     else {        executor = new SimpleExecutor(this, transaction);    }    if (cacheEnabled) {        executor = new CachingExecutor(executor);    }        // 植入插件    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;}如上,newExecutor 办法在创立好 Executor 实例后,紧接着通过拦截器链 interceptorChain 为 Executor 实例植入代理逻辑。那上面咱们看一下 InterceptorChain 的代码是怎么的。 public class InterceptorChain {    private final List<Interceptor> interceptors = new ArrayList<Interceptor>();    public Object pluginAll(Object target) {        // 遍历拦截器汇合        for (Interceptor interceptor : interceptors) {            // 调用拦截器的 plugin 办法植入相应的插件逻辑            target = interceptor.plugin(target);        }        return target;    }    /** 增加插件实例到 interceptors 汇合中 */    public void addInterceptor(Interceptor interceptor) {        interceptors.add(interceptor);    }    /** 获取插件列表 */    public List<Interceptor> getInterceptors() {        return Collections.unmodifiableList(interceptors);    }}下面的for循环代表了只有是插件,都会以责任链的形式逐个执行(别指望它能跳过某个节点),所谓插件,其实就相似于拦截器。 这里就用到了责任链设计模式,责任链设计模式就相当于咱们在OA零碎里发动审批,领导们一层一层进行审批。以上是 InterceptorChain 的全副代码,比较简单。它的 pluginAll 办法会调用具体插件的 plugin 办法植入相应的插件逻辑。如果有多个插件,则会屡次调用 plugin 办法,最终生成一个层层嵌套的代理类。形如上面: 当 Executor 的某个办法被调用的时候,插件逻辑会后行执行。执行程序由外而内,比方上图的执行程序为 plugin3 → plugin2 → Plugin1 → Executor。 plugin 办法是由具体的插件类实现,不过该办法代码个别比拟固定,所以上面找个示例剖析一下。 // TianPlugin类public Object plugin(Object target) {    return Plugin.wrap(target, this);}//Pluginpublic static Object wrap(Object target, Interceptor interceptor) {    /*     * 获取插件类 @Signature 注解内容,并生成相应的映射构造。形如上面:     * {     *     Executor.class : [query, update, commit],     *     ParameterHandler.class : [getParameterObject, setParameters]     * }     */    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);    Class<?> type = target.getClass();    // 获取指标类实现的接口    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);    if (interfaces.length > 0) {        // 通过 JDK 动静代理为指标类生成代理类        return Proxy.newProxyInstance(            type.getClassLoader(),            interfaces,            new Plugin(target, interceptor, signatureMap));    }    return target;}如上,plugin 办法在外部调用了 Plugin 类的 wrap 办法,用于为指标对象生成代理。Plugin 类实现了 InvocationHandler 接口,因而它能够作为参数传给 Proxy 的 newProxyInstance 办法。 ...

December 18, 2020 · 1 min · jiezi

关于mybatis:mybatis如何集成分页插件其实很简单啦

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 本文次要内容: 大多数框架都反对插件,用户可通过编写插件来自行扩大性能,Mybatis也不例外。 在Mybatis中最闻名的就是PageHelper 分页插件,上面咱们先来应用一下这个分页插件。 如何集成分页插件Spring-Boot+Mybatis+PageHelper 。 引入pom依赖 <dependency>   <groupId>com.github.pagehelper</groupId>   <artifactId>pagehelper-spring-boot-starter</artifactId>   <version>1.2.3</version></dependency>配置分页插件配置项 pagehelper:  helperDialect: mysql  reasonable: true  supportMethodsArguments: true  params: count=countSqlservice接口代码中 PageInfo selectUsersByName(int pageIndex, int pageSize);service实现类代码中 @Overridepublic PageInfo selectUsersByName(int pageIndex, int pageSize) {    PageHelper.startPage(pageIndex, pageSize);    List<User> users = userMapper.selectUsersByName(null);    return new PageInfo(users);}Mapper代码代码 <select id="selectUsersByName" resultMap="User">    select * from m_user    <where>        <if test="userName != null and userName != ''">            `name` = #{userName}        </if>    </where></select>List<User> selectUsersByName(@Param("userName") String userName);controller中代码 @GetMapping("/user/name")public PageInfo selectUsersByName(int pageIndex, int pageSize) {    return userService.selectUsersByName(pageIndex, pageSize);}而后咱们拜访 http://localhost:9002/user/name?pageIndex=1&pageSize=10 输入后果: 输入重要项阐明: pageNum:以后页码。pageSize:每页数。list:就是咱们返回的业务数据。total:总数据。hasNextPage:是否存在下一页。咱们在看看输入SQL: 发现其实执行了两条SQL:count和limit。 猜想分页插件实现1.这个分页插件无非就是在咱们的查问条件上拼接了个limit和做了一个count查问。 2.咱们这里应用的是Mysql作为数据库,如果是Oracle的话那就不是limit了,所以这里有多重数据库对应的计划。 3.在没有此插件的后面拦挡并做了sql和相干解决。 依据官网疾速入门插件上面是来自官网的一段话: MyBatis 容许你在映射语句执行过程中的某一点进行拦挡调用。默认状况下,MyBatis 容许应用插件来拦挡的办法调用包含: Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)ParameterHandler (getParameterObject, setParameters)ResultSetHandler (handleResultSets, handleOutputParameters)StatementHandler (prepare, parameterize, batch, update, query)这些类中办法的细节能够通过查看每个办法的签名来发现,或者间接查看 MyBatis 发行包中的源代码。如果你想做的不仅仅是监控办法的调用,那么你最好相当理解要重写的办法的行为。因为在试图批改或重写已有办法的行为时,很可能会毁坏 MyBatis 的外围模块。这些都是更底层的类和办法,所以应用插件的时候要特地当心。 通过 MyBatis 提供的弱小机制,应用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦挡的办法签名即可。 那咱们就尝试着依照官网来写一个插件。 自定义插件@Intercepts({@Signature(        type= Executor.class,        method = "update",        args = {MappedStatement.class,Object.class})})public class TianPlugin implements Interceptor {    private Properties properties = new Properties();    @Override    public Object intercept(Invocation invocation) throws Throwable {        System.out.println("老田写的一个Mybatis插件--start");        Object returnObject = invocation.proceed();        System.out.println("老田写的一个Mybatis插件---end");        return returnObject;    }}而后把插件类注入到容器中。 这里的自定义齐全是官网给出的案例。从自定义的插件类中看到有个update,咱们猜想必定是须要执行update才会被拦挡到。 拜访后面的代码:http://localhost:9002/updateUser ...

December 17, 2020 · 1 min · jiezi

关于mybatis:深入理解mybatis原理-MyBatis的架构设计以及实例分析

MyBatis是目前十分风行的ORM框架,它的性能很弱小,然而其实现却比较简单、优雅。本文次要讲述MyBatis的架构设计思路,并且探讨MyBatis的几个核心部件,而后联合一个select查问实例,深刻代码,来探索MyBatis的实现。 一、MyBatis的框架设计        注:上图很大水平上参考了iteye 上的chenjc_it 所写的博文原理剖析之二:框架整体设计 中的MyBatis架构体图,chenjc_it总结的十分好,赞一个!1.接口层---和数据库交互的形式MyBatis和数据库的交互有两种形式: a.应用传统的MyBatis提供的API;b. 应用Mapper接口 1.1.应用传统的MyBatis提供的API 这是传统的传递Statement Id 和查问参数给 SqlSession 对象,应用 SqlSession对象实现和数据库的交互;MyBatis 提供了十分不便和简略的API,供用户实现对数据库的增删改查数据操作,以及对数据库连贯信息和MyBatis 本身配置信息的保护操作。                   上述应用MyBatis 的办法,是创立一个和数据库打交道的SqlSession对象,而后依据Statement Id 和参数来操作数据库,这种形式诚然很简略和实用,然而它不合乎面向对象语言的概念和面向接口编程的编程习惯。因为面向接口的编程是面向对象的大趋势,MyBatis 为了适应这一趋势,减少了第二种应用MyBatis 反对接口(Interface)调用形式。1.2. 应用Mapper接口 MyBatis 将配置文件中的每一个<mapper> 节点形象为一个 Mapper 接口,而这个接口中申明的办法和跟<mapper> 节点中的<select|update|delete|insert> 节点项对应,即<select|update|delete|insert> 节点的id值为Mapper 接口中的办法名称,parameterType 值示意Mapper 对应办法的入参类型,而resultMap 值则对应了Mapper 接口示意的返回值类型或者返回后果集的元素类型。 依据MyBatis 的配置标准配置好后,通过SqlSession.getMapper(XXXMapper.class) 办法,MyBatis 会依据相应的接口申明的办法信息,通过动静代理机制生成一个Mapper 实例,咱们应用Mapper 接口的某一个办法时,MyBatis 会依据这个办法的办法名和参数类型,确定Statement Id,底层还是通过SqlSession.select("statementId",parameterObject);或者SqlSession.update("statementId",parameterObject); 等等来实现对数据库的操作,(_至于这里的动静机制是怎么实现的,我将筹备专门一片文章来探讨,敬请关注~_)MyBatis 援用Mapper 接口这种调用形式,纯正是为了满足面向接口编程的须要。(其实还有一个起因是在于,面向接口的编程,使得用户在接口上能够应用注解来配置SQL语句,这样就能够脱离XML配置文件,实现“0配置”)。 2.数据处理层数据处理层能够说是MyBatis 的外围,从大的方面上讲,它要实现三个性能: a. 通过传入参数构建动静SQL语句;b. SQL语句的执行以及封装查问后果集成List<E> 2.1.参数映射和动静SQL语句生成 动静语句生成能够说是MyBatis框架十分优雅的一个设计,MyBatis 通过传入的参数值,应用 Ognl 来动静地结构SQL语句,使得MyBatis 有很强的灵活性和扩展性。参数映射指的是对于java 数据类型和jdbc数据类型之间的转换:这里有包含两个过程:查问阶段,咱们要将java类型的数据,转换成jdbc类型的数据,通过 preparedStatement.setXXX() 来设值;另一个就是对resultset查问后果集的jdbcType 数据转换成java 数据类型。 ...

December 16, 2020 · 9 min · jiezi

关于mybatis:你知道目前最流行的Mybatis框架吗如何搭建呢

你晓得目前最风行的Mybatis框架吗?如何搭建呢?MyBatis 本是apache的一个开源我的项目iBatis, 2010年这个我的项目由apache software foundation 迁徙到了google code,并且改名为MyBatis 。2013年11月迁徙到Github。 iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的长久层框架。iBATIS提供的长久层框架包含SQL Maps和Data Access Objects(DAO) MyBatis是一个反对一般SQL查问,存储过程和高级映射的优良长久层框架。MyBatis打消了简直所有的JDBC代码和参数的手工设置以及对后果集的检索封装。MyBatis能够应用简略的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,一般的Java对象)映射成数据库中的记录。 mybatis不是一个齐全的orm框架,Mybatis须要程序员本人写sql,然而也存在映射(输出参数映射,输入后果映射),学习门槛mybatis比hibernate低;同时灵活性高,特地实用于业务模型易变的我的项目,应用范围广。 简略概括:更加简化jdbc代码,简化长久层,sql语句从代码中拆散,利用反射,将表中数据与java bean 属性一一映射即ORM(Object Relational Mapping 对象关系映射) 应用范畴: 在日常的开发我的项目中,如中小型我的项目,例如ERP(Crm客户关系管理系统,OA零碎),需要与关系模型绝对固定倡议应用Hibernate,对于需要不固定的我的项目,比方:互联网我的项目,倡议应用mybatis,因为须要常常灵便去编写sql语句。总之,mybatis成为当下必须学习把握的一个长久层框架。 Mybatis框架搭建的形式新建Maven我的项目log4j 日志增加resources目录下配置文件增加映射文件增加实体类Customer增加在父类工程pom获取资源测试案例实操1.新建Maven我的项目新建maven我的项目 ,pom文件增加依赖jar <!-- mybatis jar 包依赖 --><dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version></dependency><!-- 数据库驱动 --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.39</version></dependency><!-- log4j日志打印 --><dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version></dependency> 2.log4j 日志增加在src/main/resources 资源包下增加log4j日志输入properties文件,便于查看日志输入信息 # Global logging configurationlog4j.rootLogger=DEBUG, stdout#Consoleoutput...log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n 3.resources目录下配置文件增加新建mybatis.xml文件,并退出配置信息如下(数据库名mybatis,表 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> <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://127.0.0.1:3306/spring-test" /> <property name="username" value="root" /> <property name="password" value=""/> </dataSource> </environment> </environments> <!-- mapper 配置文件指定 文件数量可配置多个--> <mappers> <mapper resource="com/xxx/mapper/CustomerMapper.xml" /> </mappers></configuration> 对于标签的配置可能不会呈现主动提醒: ...

December 16, 2020 · 2 min · jiezi

关于mybatis:答了Mybatis这个问题后面试官叫我回去等通知……

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 背景前段时间在我的技术群里,大家探讨起了为什么UserMapper.java是个接口,没有具体实现类,而咱们能够间接调用其办法? 对于这个问题,我之前面试过一些人,很多人是这么答复的: 1.我领导叫咱们应用Mybatis,大家都这么用就这么用了(没想过,反正就这么用)。2.尽管我不晓得具体是怎么实现的,但我感觉必定是……(此处略去若干的漫天猜测),然而也不对啊,难道是……(再次略去若干似懂非懂)。 3.应用动静代理实现的(而后就没有下文了)。 对于下面的三种答复,后面两种咱们就没必要往下聊了。 然而第三种答复,就有必要往下问:那你说说动静代理有哪些实现形式?Mybatis应用的是哪一种? 如果这个问题你还能答复上来,那么还会持续问:UserMapper.java中大办法能不能重载? 如果你能答复下面的问题,本文就没必要往下看了,曾经不适宜你了。 问题剖析先来看一张图,这图里的代码就是咱们后面写的demo: 为什么一个接口就能和一个xml文件给绑定的呢?这就是明天咱们要聊的话题。 可能很多小伙伴不相熟ibatis,2010年之前,还没有Mybatis,之后ibatis便成了当初的Mybatis,如果有趣味的敌人,能够看到Mybatis中的包目录。 这个包目录中就还是ibatis,并且ibatis的作者当初就在腾讯下班,开发英雄联盟LOL。 如果有腾讯的小伙伴能够打听打听哈,大佬就在身边。言归正传。 Mapper层在Mybatis中当初是接口模式就搞定了,而在ibatis时代还是必须要有实现类的,我记得2012年的时候,应用的就是ibatis,Dao(Mapper)必须要有实现类。上面咱们就来看看Mybatis中是怎么做的。 应用案例持续应用咱们上一节中的代码。 controller service实现类中 打一个断点,而后应用debug模式启动我的项目。并拜访: http://localhost:9002/test userMapper=org.apache.ibatis.binding.MapperProxy@6da21078 发现Mybatis给UserMapper.java生成了一个代理对象,并且从名字上能够看出是JDK动静代理。 对于动静代理请,这里我举荐我之前写过的一篇文章: https://gitbook.cn/m/mazi/act... 其实,又差不多回到了ibatis时代,只是Mybatis中是通过动静代理的形式生成的代理类不是咱们开发的,而是通过JDK动静代理生成的代理类。 上面咱们也应用JDK动静代理来模仿一把。 public class MapperProxy implements InvocationHandler {    @SuppressWarnings("unchecked")    public <T> T newInstance(Class<T> clz) {        return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[] { clz }, this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        if (Object.class.equals(method.getDeclaringClass())) {            try {                // 诸如hashCode()、toString()、equals()等办法,将target指向以后对象this                return method.invoke(this, args);            } catch (Throwable t) {            }        }        // 投鞭断流        return new User((Integer) args[0], "田维常", 22);    }}再写一个测试类 import com.tian.mybatis.entity.User;import com.tian.mybatis.mapper.UserMapper;public class TestProxy {    public static void main(String[] args) {        MapperProxy proxy = new MapperProxy();        UserMapper mapper = proxy.newInstance(UserMapper.class);        User user = mapper.selectById(999);        System.out.println(user);        System.out.println(mapper.toString());    }}输入 User{id=999, userName='田维常', age=22, gender=null}com.tian.mybatis.proxy.MapperProxy@39a054a5这便是Mybatis主动映射器Mapper的底层实现原理。 然而在Mybatis中,远远不是这么简略的,然而实质就是这样的。 上面咱们就来大抵剖析一下Mybatis中的这个流程。 接口Mapper内的办法能重载吗?相似上面: public User getUserById(Integer id);public User getUserById(Integer id, String name);答案:不能 因为Mybatis中是应用package+Mapper+method全限名作为key,去xml内寻找惟一sql来执行的。 相似:key=com.tian.mybatis.UserMapper.getUserById,那么,重载办法时将导致矛盾。 对于Mapper接口,Mybatis禁止办法重载(overLoad) 。 在MapperMethod类的动态外部类中SqlCommand中有个resolveMappedStatement办法。 在Configuration中有个属性,就是我的项目启动的时候,会把Mapper.xml中信息解析到这个属性里,以咱们指定的namespace+method作为key放到Map外面,前面咱们调用Mapper接口动静类的某个办法时候再去map获取。 protected final Map<String, MappedStatement> mappedStatements 就是应用类的全路径名.办法作为key寄存到Map中的。 总结罕用动静代理形式:JDK动静代理和CGlib动静代理。 Mybatis是采纳JDK动静代理+反射+xml来解决接口绑定的,为咱们创立能够调用的代理对象。 咱们的Mapper中的办法是相对不能重载的。 举荐浏览 把握Mybatis动静映射,我可是下了功夫的 《写给大忙人看的JAVA核心技术》.pdf

December 16, 2020 · 1 min · jiezi

关于mybatis:让人纠结的mybatis源码我该拿你怎么办

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 背景最近,听到很多吐槽:看到源码,心中就感到非常纠结、特地懊恼。 为什么纠结?因为面试的时候,面试官很喜爱问:你看过什么框架源码?JDK源码也行。 这时候,如果答复没有看过,尽管没让你立马回去等告诉。但兴许,你在面试官心中的印象就大打折扣了。 如果答复看过,并且还能较为流利地说上一番,那面试官或者会在心里赞叹:小伙子能够呀!(PS:很多面试官本人也没看过,或者看过很多也忘了)。 总之,你要是能说上个123,那给面试官的印象就会杠杠滴好。何愁工作?何愁加薪? 为什么烦?大部分人的状况是:源码不是没有看过,而是每次只看得下一部分。为什么只看得下一部分呢?通常有上面三种起因: 不足技术撑持。看源码是须要技术撑持的,不是轻易一个小白也能看懂的。没有一些技术撑持,你顶多看看一小段,而后就看不下去,于是就放弃了。不足正确心态。看源码的确挺无聊的,如果大家心态没放好,把它当做一种工作去做,必定感觉无聊,而且很容易放弃。不足足够工夫。的确很忙,常常看了一部分后,天天加班,没工夫看,忙完后又忘了之前正在看的某某源码。集体倡议对于个别java程序员来说,浏览源码之前到底须要些什么技能呢? 集体倡议,以下基础知识必须会一些: 会设计模式:包含单例模式、工厂模式、代理模式、装璜器模式、责任链模式、模板办法模式等。 会猜想:大胆的猜想,在看源码的时候,多站在高层次想想,如果你是Mybatis的设计者,你会怎么设计? 会挑重点看:别被没写参数校验、类型校验等问题卡在那里。 会画图:流程图、类图等,找一些在线画图工具,媒体画完就保留下来,如果中途一段时间不看了,再回头看看这些图,也能疾速持续。 Mybatis源码剖析明天,咱们就来看看Mybatis源码的浏览,具体举个例子来看看: 案例和疑难从咱们最后的demo中开始: public static void main(String[] args) {        String resource = "mybatis-config.xml";        InputStream inputStream = null;        SqlSession sqlSession = null;        try {            inputStream = Resources.getResourceAsStream(resource);            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);            sqlSession = sqlSessionFactory.openSession();             User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectById", 1);            System.out.println(user);         } catch (Exception e) {            e.printStackTrace();        } finally {            try {                inputStream.close();            } catch (IOException e) {                e.printStackTrace();            }            sqlSession.close();        }    }对于获取数据流inputStream,这个咱们就不探讨了,咱们次要关注重点。 对于下面demo,咱们能够分成五个步骤: 第一步,创立一个工厂类sqlSessionFactory。 配置文件的解析就是在这里实现的。包含mybatis-config.xml和咱们的Mapper.xml映射器文件。这一步咱们关怀的内容是:解析的时候做了什么?产生了什么对象,解析的后果放在哪里的。因为这将意味着,咱们前面应用的时候去哪里获取这项配置项内容。 第二步,通过SqlSessionFactory创立一个SqlSession。 那么问题来了,SqlSession中定义了各种增删改查的API,是给客户端调用,返回的是什么实现类?除了SqlSession以外,咱们还创立了什么对象,创立了什么环境? 第三步,获取到一个Mapper对象。 问题来了UserMapper.java是一个接口,并没有为它创立实现类,那又是怎么被实例化的呢?咱们应用的这个Mapper对象到底是什么对象呢?为什么要从SqlSession里去获取呢?为什么传进去一个接口,而后还要用一个接口去接管呢? 第四步,调用接口办法。 问题是咱们的接口没有实现类,为什么就能够间接调用它的办法呢?那它调用的是谁的办法呢?是如何把SQL给关联起来的呢?是如何获取到数据的呢? 第五步,敞开相干资源。 开始源码剖析过程因为波及到内容较多,上面就用几张图来展现整个流程。咱们能够通过这几张图来疾速翻阅Mybatis的源码。 第一步 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);这一句代码的整个流程如下: 依据inputStream,解析配置文件,创立出一个DefaultSqlSessionFactory默认的SqlSessionFactory 实现类。构建出一个工厂类,这个工厂类专门用来创立SqlSession对象的。 第二步 SqlSession  sqlSession = sqlSessionFactory.openSession();获取SqlSession的整个流程如下: 第三步 User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectById", 1);第三步和第四步就是这行代码画的。 这一步是返回一个映射器代理类,映射器代理类专门用来给UserMapper接口和UserMapper.xml绑定的代理类。创立进去的代理类就能够实例化了,而后就能够调用UserMapper接口的方了。 第四步:调用代理对象执行SQL的整个过程。 第五步,敞开资源。 心愿大家依照这个流程,找点源码看看。听他人的看他人永远是他人的。 动起来吧! 总结其实,没什么好总结的。好好领悟一下为什么纠结吧!同时心愿能看看我的倡议,作为一个过来人,心愿能帮忙大家少走弯路。 最初,对于Mybatis源码浏览的整个流程,最好是本地环境搭起来,而后搞个demo,debug模式一步一步走起。 举荐浏览 《图解数据结构》.pdf 7种启动Spring Boot我的项目的形式,一次性打包说给你听 JVM真香系列:图解垃圾回收器

December 14, 2020 · 1 min · jiezi

关于mybatis:mybatis源码阅读步骤这篇文章说明白了

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 很多人看源码都不晓得如何看,明天来教教大家如何看源码。 前提是咱们须要对整个Mybatis的原理、工作流程和模块进行一个整体的直通晓,另外还要有应用教训。 倡议先看这两篇文章: 本文次要内容: 源码下载如何下载源码?进入官网 https://mybatis.org/mybatis-3... 办法一 跳转到github上,咱们能够在github高低源码 https://github.com/mybatis/my... 下载源码 办法二 也能够通过 https://github.com/mybatis/my... 下载对应版本jar和源码: 下载到本地后,解压到本人的本地目录,而后应用eclipse或者IDEA导入。 发现Mybatis的包目录多大20个,上面咱们就来说说这些包的关系和对应的作用。 包分成三大类 根底性能包这些包用来为其余包提供一些外围根底性能,如文件读取性能、反射操作性能等。这些包的特点是性能绝对独立,与业务逻辑耦合小。 上面就是根底性能相干的包目录: 配置解析包这些包用来实现配置解析、存储等工作。这些包中的办法次要在零碎初始化阶段运行。 上面就是配置相干的包目录: 外围操作包这些包用来实现数据库操作。在工作过程中,这些包可能会依赖根底性能包提供的根底性能和配置解析包提供的配置信息。这些包中的办法次要在数据库操作阶段运行。 外围解决层次要做了这四件事: 把接口中传入的参数解析并映射成JDBC类型;解析xml文件中的SQL语句,包含插入参数和动静SQL的生成;执行SQL语句;处理结果集,并映射成Java对象。留神:插件也属于核心层,这是由它的工作形式和拦挡的对象决定的。 上面就是核心层对应的包目录: 具体包介绍reflection 包Java 中的反射尽管功能强大,但对大多数开发人员来说,写出高质量的反射代码还是 有肯定难度的。 MyBatis 中专门提供了反射模块,该模块对 Java 原生的反射进行了良好的封装,提了更加简洁易用的 API,不便下层调用,并且对反射操作进行了一系列优化,例如缓存了类的元数据,进步了反射操作的性能。 Java反射机制次要提供了以下性能。· 在运行时判断任意一个对象所属的类;· 在运行时结构任意一个类的对象;· 在运行时批改任意一个对象的成员变量;· 在运行时调用任意一个对象的办法。 举荐一篇反射的文章: type 包① MyBatis 为简化配置文件提供了别名机制,该机制是类型转换模块的次要性能之一。 ② 类型转换模块的另一个性能是实现 JDBC 类型与 Java 类型之间的转换,该性能在为 SQL 语句绑定实参以及映射查问后果集时都会波及: 在为 SQL 语句绑定实参时,会将数据由 Java 类型转换成 JDBC 类型。而在映射后果集时,会将数据由 JDBC 类型转换成 Java 类型。logging 包无论在开发测试环境中,还是在线上生产环境中,日志在整个零碎中的位置都是十分重要的。良好的日志性能能够帮忙开发人员和测试人员疾速定位 Bug 代码,也能够帮忙运维人员疾速定位性能瓶颈等问题。目前的 Java 世界中存在很多优良的日志框架,例如 Log4j、 Log4j2、Slf4j 等。 ...

December 13, 2020 · 2 min · jiezi

关于mybatis:啦la

`<?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="RegulationDO"> <sql id="queryParam"> <where> <if test="id != null and id != ''"> r.ID = #{id} </if><if test="isLock != null and isLock != ''"> AND r.IS_LOCK = #{isLock} </if><if test="checkPoint != null and checkPoint != ''"> AND r.CHECK_POINT = #{checkPoint} </if><if test="riskEventType != null and riskEventType != ''"> AND r.RISK_EVENT_TYPE = #{riskEventType} </if><choose> <when test="ruleTypeArray != null and ruleTypeArray.length > 0"> AND r.RULE_TYPE in ...

December 11, 2020 · 6 min · jiezi

关于mybatis:Mybatis动态映射这次终于搞明白了

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 动静 SQL 是 MyBatis 的弱小个性之一。如果你应用过 JDBC 或其它相似的框架,你应该能了解依据不同条件拼接 SQL 语句有多苦楚,例如拼接时要确保不能遗记增加必要的空格,还要留神去掉列表最初一个列名的逗号。利用动静 SQL,能够彻底解脱这种苦楚。 应用动静 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的弱小的动静 SQL 语言,MyBatis 显著地晋升了这一个性的易用性。 如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动静 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,须要花工夫理解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素品种,当初要学习的元素品种比原来的一半还要少。 if:利用if实现简略的条件抉择。choose(when,otherwise):相当于java中的switch语句,通常与when和otherwise搭配。 set:解决动静更新语句。 trim:灵便的去除多余的关键字。 foreach:迭代一个汇合,通常用于in条件。 理论工作中很多时候,这几个标签都是组合着应用。 明天的演示应用的是Spring-Boot+Mybatis进行演示,对于Spring-Boot整合Mybatis举荐: if+where实现多条件查问创立一种昂数据库表: CREATE TABLE `m_user` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `name` varchar(255) DEFAULT NULL,  `age` int(11) DEFAULT NULL,  `gender` int(11) DEFAULT NULL COMMENT '0:女生 1:男生',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;初始化几条数据: 先来看UserMapper.xml文件: <mapper namespace="com.tian.mybatis.mapper.UserMapper">    <resultMap id="User" type="com.tian.mybatis.entity.User">        <id column="id" property="id"/>        <result column="name" property="userName"/>    </resultMap>    <select id="selectUserById"  resultMap="User">        select * from m_user        <where>            <if test="id != null">                id = #{id}            </if>            <if test="name != null and name != ''">                and `name` = #{name}            </if>        </where>    </select></mapper>UserMapper.java内容: import com.tian.mybatis.entity.User;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;@Repositorypublic interface UserMapper {    User selectUserById(@Param("name") String userName, @Param("id") Integer id);}UserService.java内容: public interface UserService {    User selectUserById(String userName, Integer id);}UserServiceImpl.java内容: import com.tian.mybatis.entity.User;import com.tian.mybatis.mapper.UserMapper;import com.tian.mybatis.service.UserService;import org.springframework.stereotype.Service;import javax.annotation.Resource;@Servicepublic class UserServiceImpl implements UserService {    @Resource    private UserMapper userMapper;    @Override    public User selectUserById(String userName, Integer id) {        return userMapper.selectUserById(userName, id);    }}UserController.java内容: import com.tian.mybatis.entity.User;import com.tian.mybatis.service.UserService;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestControllerpublic class UserController {    @Resource    private UserService userService;    @GetMapping("/test")    public User selectUserById() {        return userService.selectUserById("tian", 1);    }}Application.java内容: import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("com.tian.mybatis.mapper")public class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}把我的项目启动起来,而后进行拜访/test。 http://localhost:9002/test 返回: 下面的这个案例也是咱们工作中的代码案例,咱们工作但局部都应用这种形式。 上面的所有演示都是基于下面这些代码进行调整而成的。 回到正题。 ...

December 11, 2020 · 1 min · jiezi

关于mybatis:Mybatis-中xml和注解映射so-easy啦

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 MyBatis 提供了XML配置和注解配置两种形式。明天就来搞搞这两种形式是如何实现的。 MyBatis 的真正弱小在于它的语句映射,这是它的魔力所在。因为它的异样弱小,映射器的 XML 文件就显得绝对简略。如果拿它跟具备雷同性能的JDBC 代码进行比照,你会立刻发现省掉了将近 95% 的代码。MyBatis 致力于缩小应用老本,让用户能更专一于 SQL 代码。来自官网。 Mybatis映射九个顶级元素: mapper:映射文件的根节点,只有一个属性namespace(命名空间),作用如下:用于辨别不同的mapper,全局惟一。绑定DAO接口,即面向接口编程,当绑定一个接口,就不必写此接口的实现类,会通过接口的齐全限定名找到对应的mapper配置来执行SQL语句,所以,namespace的命名必须要写接口的齐全限定名。cache:配置给定命名空间的缓存。cache-ref:从其余命名空间援用缓存配置。resultMap:用来形容数据库后果集和对象的对应关系。sql:能够重用的SQL块,也能够被其余语句援用。通常时寄存一些专用性的SQL。insert:映射插入语句。update:更新映射语句。delete:删除映射语句。select:映射查问语句。 xml形式九个顶级映射元素对应标签: `<mapper namespace="com.tian.mybatis.mapper.UserMapper">` `<resultMap id="" type=""></resultMap>` `<sql id=""></sql>` `<cache blocking="" ></cache>` `<cache-ref namespace=""></cache-ref>` `<select id="selectUserById"></select>` `<insert id="insert" ></insert>` `<update id=""></update>` `<delete id=""></delete>``</mapper>`select详解 能够看得出,前面可选项还是蛮多的。上面是官网对每项的解释。 select应用案例`<?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.tian.mybatis.mapper.UserMapper">` `<select id="selectUserById"  resultType="com.tian.mybatis.entity.User" parameterType="int" >` `select * from m_user where id = #{id}` `</select>``</mapper>`id必须在这个Mapper中是惟一的,能够被用来援用这条语句 ,这个id必须与只对应的是XxxMapper.java中的办法,必须是一一对应。返回类型:User类型,resultType:查问语句返回后果类型的齐全限定名或别名。别名应用形式和parameterType是一样的。参数:整形,示意查问语句传入参数的类型和齐全限定名或别名。反对根底数据类型和简单数据类型。{参数名}:通知MyBatis生成的PreparedStatement参数,绝对于JDBC中,改参数被标识为‘?’。别名与参数映射类型如下: 返回类型中别名的应用,留神: 如果是咱们的entity类,那么resultType是无奈应用别名的,只能应用resultMap才能够应用别名。 `<?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.tian.mybatis.mapper.UserMapper">` `<resultMap id="User" type="com.tian.mybatis.entity.User"/>` `<select id="selectUserById"  resultMap="User" parameterType="int" >` `select * from m_user where id = #{id}` `</select>``</mapper>`然而如果应用的下面映射表里,也能够间接应用别名。 数据库里有两条数据: UserMapper.xml `<?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.tian.mybatis.mapper.UserMapper">` `<select id="countUser" resultType="int">` `select count(1) from m_user` `</select>``</mapper>`UserMapper.java `import com.tian.mybatis.entity.User;``public interface UserMapper {` `int countUser();``}`测试类: `public class MybatisApplication {` `public static final String URL = "jdbc:mysql://localhost.com:3306/mblog?useUnicode=true";` `public static final String USER = "root";` `public static final String PASSWORD = "123456";` `public static void main(String[] args) {` `String resource = "mybatis-config.xml";` `InputStream inputStream = null;` `SqlSession sqlSession = null;` `try {` `inputStream = Resources.getResourceAsStream(resource);` `//工厂模式` `SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);` `//获取sql操作会话` `sqlSession = sqlSessionFactory.openSession();` `//结构对象(这里比拟非凡,这里结构对象的形式前面会专门分享)` `UserMapper userMapper =  sqlSession.getMapper(UserMapper.class);` `//查问统计` `System.out.println(userMapper.countUser());` `} catch (Exception e) {` `e.printStackTrace();` `} finally {` `try {` `inputStream.close();` `} catch (IOException e) {` `e.printStackTrace();` `}` `sqlSession.close();` `}` `}``}`输入:2 ...

December 10, 2020 · 1 min · jiezi

关于mybatis:小师妹如果想快速入门Mybatis把这篇甩给她就够了

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 本文次要内容: 传统JDBC传统JDBC编码格局`public class DataBaseUtil {` `public static final String URL = "jdbc:mysql://localhost:3306/mblog";` `public static final String USER = "root";` `public static final String PASSWORD = "123456";` `public static void main(String[] args) throws Exception {` `Class.forName("com.mysql.jdbc.Driver");` `//2.` `Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);` `//3.` `Statement stmt = conn.createStatement();` `//4.` `ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");` `//如果有数据,rs.next()返回true` `while(rs.next()){` `System.out.println("name: "+rs.getString("name")+" 年龄:"+rs.getInt("age"));` `}` `}``}`下面代码中常识为了展现JDBC整个过程(异样和资源是简略粗犷的解决了,咱们关注的点不在这两个)。 大抵能够分为六个步骤: 加载驱动程序取得数据库连贯创立一个Statement对象操作数据库,实现增删改查获取后果集敞开资源从应用层面来说,采纳原生态的JDBC在我的项目中应用起来老本还是很高的。如果咱们的我的项目中的业务绝对比较复杂,数据库表也绝对较多,各种操作数据库的增删改查的办法也会随之多起来,那么这样的代码反复次数会十分之多。 传统JDBC的问题创立数据库的连贯存在大量的硬编码,执行statement时存在硬编码.频繁的开启和敞开数据库连贯,会重大影响数据库的性能,节约数据库的资源.存在大量的重复性编码为了解决以上问题,就诞生了各种各样替换JDBC的产品。即就是ORM框架。 什么是ORM?全称为Object Relational Mapping。对象-映射-关系型数据库。对象关系映射(,简称ORM,或O/RM,或O/R mapping),用于实现面向对象编程语言里不同类型零碎的数据之间的转换。简略的说,ORM是通过应用形容对象和数据库之间映射的元数据,将程序中的对象与关系数据库互相映射。 ORM提供了实现长久化层的另一种模式,它采纳映射元数据来形容对象关系的映射,使得ORM中间件能在任何一个利用的业务逻辑层和数据库层之间充当桥梁。 咱们的我的项目中是这样的: 比如说:Apache DbUtils、Spring JDBC、 Hibernate、Ibatis(Mybatis的前生)、Spring Data Jpa等等。 目前最为风行的Mybatis和Spring Data Jpa。本系列咱们先解说Mybatis,Jpa前面再讲。 ORM的优缺点长处1.进步了开发效率。因为ORM能够主动对Entity对象与数据库中的Table进行字段与属性的映射,所以咱们理论可能曾经不须要一个专用的、宏大的数据拜访层。2.ORM提供了对数据库的映射,不必sql间接编码,可能像操作对象一样从数据库获取数据。 毛病就义程序的执行效率和会固定思维模式,升高了开发的灵活性。 从系统结构上来看,采纳ORM的零碎个别都是多层零碎,零碎的档次多了,效率就会升高。ORM是一种齐全的面向对象的做法,而面向对象的做法也会对性能产生肯定的影响。在咱们开发零碎时,个别都有性能问题。性能问题次要产生在算法不正确和与数据库不正确的应用上。ORM所生成的代码个别不太可能写出很高效的算法,在数据库利用上更有可能会被误用,次要体现在对长久对象的提取和和数据的加工解决上,如果用上了ORM,程序员很有可能将全副的数据提取到内存对象中,而后再进行过滤和加工解决,这样就容易产生性能问题。在对对象做长久化时,ORM个别会长久化所有的属性,有时,这是不心愿的。但ORM是一种工具,工具的确能解决一些反复,简略的劳动。这是不可否认的。但咱们不能指望工具能一劳永逸的解决所有问题,有些问题还是须要非凡解决的,但须要非凡解决的局部对绝大多数的零碎,应该是很少的。 MyBatis 是什么?如果在面试的时候被问到,只有你说出上面三种即可。 MyBatis 是一款优良的长久层框架,它反对自定义 SQL、存储过程以及高级映射。MyBatis 罢黜了简直所有的 JDBC 代码以及设置参数和获取后果集的工作。 MyBatis 能够通过简略的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,一般老式 Java 对象)为数据库中的记录。 来自官网。 MyBatis的长处和毛病长处: (1)基于SQL语句编程,相当灵便,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于对立治理;提供XML标签,反对编写动静SQL语句,并可重用。 ...

December 9, 2020 · 2 min · jiezi

关于mybatis:MyBatis-if-标签的坑居然被我踩到了

事件的起因是这样的,需要是按条件查数据而后给前端展现就行了,写的时候想着挺简略的,不就是应用 MyBatis 动静 SQL 去查问数据吗? 事实还是很残暴的,等我写完上完 UAT 后,前端同学说依据state查的数据与现实的数据不统一,这个state过后设计时只有两个值:0和1。 /** * 数据状态 */@Range(min = 0, max = 1, message = "状态只能为0(未解决),1(已解决)")private Integer state;现实状况下通过前端传递过去的值,而后进行sql查问就能够了: <if test="req.state != null and req.state != ''"> AND md.state = #{req.state}</if>下面的sql首先判断state不为空且不为空字符串时,而后增加比拟state字段。初步看下来if判断没什么问题,然而我传递进去的req.state是Integer型的,认真查看req.state != null没故障,而后发现req.state != ''应用Integer与空字符串做比拟。 前端在查问的时如果没有传递req.state那req.state != null 这里不会满足,然而前端传递了一个0过去的时候req.state != ''竟然返回的是false也就是说在MyBatis的if语法中0是等于空字符串的: { "state": 0}这样的比拟没有报错,也是有点想不通了,没方法只能去看MyBatis源码找出这起因。 查看 MyBatis 源码MyBatis 其余源码的查找过程就不具体说了,这里间接找到XMLScriptBuilder类,找到if语法的解析过程,而后一步步的探索0 == ''的起因。 XMLScriptBuilder会解析trim、if等 MyBatis 反对的语法,它的解析原理是通过NodeHandler来别离解析不同的标签: private void initNodeHandlerMap() { nodeHandlerMap.put("trim", new TrimHandler()); nodeHandlerMap.put("where", new WhereHandler()); nodeHandlerMap.put("set", new SetHandler()); nodeHandlerMap.put("foreach", new ForEachHandler()); nodeHandlerMap.put("if", new IfHandler()); nodeHandlerMap.put("choose", new ChooseHandler()); nodeHandlerMap.put("when", new IfHandler()); nodeHandlerMap.put("otherwise", new OtherwiseHandler()); nodeHandlerMap.put("bind", new BindHandler()); }因为是不正解的语法是if标签,查看IfHandler就好了,其余当初略过就好。 ...

December 9, 2020 · 2 min · jiezi

关于mybatis:SpringBootMyBatisSpring-技术整合实现商品模块的CRUD操作详细

业务形容基于Spring,MyBatis,SpringBoot,Thymeleaf技术实现商品模块的增删改查操作。 我的项目环境初始化筹备工作1. MySQL(5.7)2. JDK (1.8)3. Maven (3.6.3)4. STS(4.7.1)数据库初始化关上mysql控制台,而后按如下步骤执行goods.sql文件。 第一步:登录mysql。 mysql –uroot –proot第二步:设置控制台编码方式。 set names utf8;第三步:执行goods.sql文件(切记不要关上文件复制到mysql客户端运行)。 source d:/goods.sql其中goods.sql文件内容如下: drop database if exists dbgoods;create database dbgoods default character set utf8;use dbgoods;create table tb_goods( id bigint primary key auto_increment, name varchar(100) not null, remark text, createdTime datetime not null)engine=InnoDB;insert into tb_goods values (null,'java','very good',now());insert into tb_goods values (null,'mysql','RDBMS',now());insert into tb_goods values (null,'Oracle','RDBMS',now());insert into tb_goods values (null,'java','very good',now());insert into tb_goods values (null,'mysql','RDBMS',now());insert into tb_goods values (null,'Oracle','RDBMS',now());insert into tb_goods values (null,'java','very good',now());创立我的项目并增加依赖基于STS创立第一步:基于start.spring.io 创立我的项目并设置根本信息 ...

December 6, 2020 · 3 min · jiezi

关于mybatis:Mybatis6-Mybatis插入数据后自增id怎么获取

代码间接放在Github仓库【https://github.com/Damaer/Myb... 】 须要申明的是:此Mybatis学习笔记,是从原始的Mybatis开始的,而不是整合了其余框架(比方Spring)之后,集体认为,这样能对它的性能,它能帮咱们做什么,有更好的了解,前面再缓缓叠加其余的性能。咱们晓得很多时候咱们有一个需要,咱们须要把插入数据后的id返回来,以便咱们下一次操作。 其实一开始的思路是我插入之后,再执行一次select,依据一个惟一的字段来执行select操作,然而Student这个类如果插入后再依据名字或者年龄查出来,这根本就是不可行的!!!重名与同年龄的人肯定不少。咱们的测试方法如下,咱们能够看到插入前是没有值的,插入后就有了值: /** * 测试插入后获取id */@Testpublic void testinsertStudentCacheId(){ Student student=new Student("helloworld",17,85); System.out.println("插入前:student="+student); dao.insertStudentCacheId(student); System.out.println("插入后:student="+student);}useGeneratedKeys 设置主键自增 <insert id="insertStudentCacheId" useGeneratedKeys="true" keyProperty="id" parameterType="Student"> insert into student(name,age,score) values(#{name},#{age},#{score}) </insert>须要留神的点: 1.useGeneratedKeys="true"示意设置属性自增2.keyProperty="id"设置主键的字段3.parameterType="Student"设置传入的类型4.留神:尽管有返回类型,然而咱们不须要手动设置返回的类型,这个是由框架帮咱们实现的,所以对应的接口办法也是没有返回值的,会批改咱们插入的对象,设置id值。5.实体类中id属性字段肯定须要set以及get办法 应用selectKey 查问主键 <insert id="insertStudentCacheId" parameterType="Student"> insert into student(name,age,score) values(#{name},#{age},#{score}) <!-- 指定后果类型resultType,keyProperty是属性,主动返回到属性id中,order是秩序,after是指获取id是在于插入后 --> <selectKey resultType="int" keyProperty="id" order="AFTER"> select @@identity </selectKey> </insert>或者写成: <insert id="insertStudentCacheId" parameterType="Student"> insert into student(name,age,score) values(#{name},#{age},#{score}) <!-- 指定后果类型resultType,keyProperty是属性,主动返回到属性id中,order是秩序,after是指获取id是在于插入后 --> <selectKey resultType="int" keyProperty="id" order="AFTER"> select LAST_INSERT_ID() </selectKey> </insert>两种形式的后果:[](http://markdownpicture.oss-cn... 留神要点: 1.最外层的<insert></insert>没有返回属性(resultType),然而外面的<selectKey></selectKey>是有返回值类型的。2.order="AFTER"示意先执行插入,之后才执行selectkey语句的。3.select @@identity和select LAST_INSERT_ID()都示意选出刚刚插入的最初一条数据的id。4.实体类中id属性字段肯定须要set以及get办法5.此时,接口中仍不须要有返回值,框架会主动将值注入到咱们insert的那个对象中,咱们能够间接应用就能够了。其实,咱们的接口中能够有返回值,然而这个返回值不是id,而是示意插入后影响的行数,此时sql中仍和下面一样,不须要写返回值。 <insert id="insertStudentCacheIdNoReturn" parameterType="Student"> insert into student(name,age,score) values(#{name},#{age},#{score}) <!-- 指定后果类型resultType,keyProperty是属性,主动返回到属性id中,order是秩序,after是指获取id是在于插入后 --> <selectKey resultType="int" keyProperty="id" order="AFTER"> select LAST_INSERT_ID() </selectKey> </insert>接口中: ...

December 5, 2020 · 1 min · jiezi

关于mybatis:Mybatis5-Mybatis多种增删改查那些你会了么

后面咱们学会了Mybatis如何配置数据库以及创立SqlSession,那怎么写呢?crud怎么写? 代码间接放在Github仓库【https://github.com/Damaer/Myb... 】 须要申明的是:此Mybatis学习笔记,是从原始的Mybatis开始的,而不是整合了其余框架(比方Spring)之后,集体认为,这样能对它的性能,它能帮咱们做什么,有更好的了解,前面再缓缓叠加其余的性能。我的项目的目录如下: 创立数据库:初始化数据,SQL语句如下(也就是resource下的test.sql) #创立数据库CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;#创立数据表CREATE TABLE `student` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(20) NOT NULL ,`age` INT NOT NULL , `score` DOUBLE NOT NULL , PRIMARY KEY (`id`)) ENGINE = MyISAM;应用maven治理我的项目,pom.xml文件治理依赖jar包: <?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>com.test</groupId> <artifactId>test</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- mybatis外围包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.3.0</version> </dependency> <!-- mysql驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.29</version> </dependency> <!-- junit测试包 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- 日志文件治理包 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency> </dependencies></project>与数据库中绝对应的实体类Student: ...

December 5, 2020 · 5 min · jiezi

关于mybatis:SpringBootMyBatisSpring-技术整合实现商品品牌模块的CRUD操作

业务形容需要形容任何一个电商零碎中都有一个商品子系统,而与商品相关联都会有一个品牌信息管理,在以后业务零碎设计中咱们就是要对商品品牌信息的治理进行设计和实现. 业务架构剖析在品牌(Brand)信息管理中就是要实现对商品品牌信息的增加,批改,查问,删除等业务,如图所示: 业务根本原型设计基于品牌业务形容,对品牌模块的业务原型进行剖析和设计,如图所示: 品牌列表页面 品牌编辑页面 我的项目技术架构剖析及设计在品牌治理模块实现过程,咱们采纳典型的C/S架构进行实现.客户端咱们基于浏览器进行实现,服务端采纳tomcat,数据库应用MySQL.具体应用层基于MVC分层架构进行实现. 我的项目技术栈利用剖析及选型客户端技术:html,css,javascript,bootstrap服务端技术:hikaricp,mybatis,spring,springboot,thymeleaf数据库技术:mysql,sql开发工具集:jdk1.8,maven3.6.3,idea2020.2我的项目品牌模块外围API剖析与设计基于分层架构设计思维,现对品牌API进行设计,如图所示: 数据库及表的剖析与设计设计并创立数据库如果数据库已存在,则先删除数据库,代码如下: drop database if exists dbbrand;创立新的数据库,代码如下: create database dbbrand default character set utf8;设计并创立品牌(Brand)表关上数据库,语句如下: use dbbrand;在dbbrand数据库中创立品牌表. create table tb_brand( id bigint primary key auto_increment, name varchar(100) not null, remark text, createdTime datetime not null)engine=InnoDB;基于SQL脚本执行数据库初始化将数据库设计脚本写到brand.sql中,而后按如下步骤执行: 关上mysql自带客户端,登录mysql,指令如下: mysql -uroot -proot设置客户端编码,指令如下: set names utf8;执行sql脚本,指令如下: source d:/brand.sql脚本执行胜利当前,在客户端查问数据之前,先执行如下语句: set names gbk;我的项目环境初始化筹备操作1)JDK 1.82)Maven 3.6.33)IDEA 2020.24)MySQL 5.7+ 创立我的项目Module关上idea,而后基于设计,创立我的项目module,如图所示: 增加我的项目Module依赖MySQL 驱动<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency>Srping Jdbc 提供了HikariCP连接池<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId></dependency>MyBatis 资源<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version></dependency>Spring Web 依赖 (内置一个tomcat服务)<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>Thymeleaf 依赖 (html模板引擎)<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>我的项目Module根底配置关上我的项目Module配置文件application.properties,并增加如下内容: ...

December 3, 2020 · 4 min · jiezi

关于mybatis:Mybatis3-Mybatis使用工具类读取配置文件以及从属性读取DB信息

代码间接放在Github仓库【https://github.com/Damaer/Myb... 】,可间接运行,就不占篇幅了。1.应用工具类获取sqlSession实例对象在上一个demo中,解决了多个namespace的问题,那么咱们能够看到代码还是会有肯定的冗余,比方上面这段代码中咱们每一个增删改查操作都须要读取一遍配置文件: public class StudentDaoImpl implements IStudentDao { private SqlSession sqlSession; public void insertStu(Student student) { try { InputStream inputStream; inputStream = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream); sqlSession=sqlSessionFactory.openSession(); sqlSession.insert("mapper1.insertStudent",student); sqlSession.commit(); } catch (IOException e) { e.printStackTrace(); }finally { if(sqlSession!=null){ sqlSession.close(); } } }}咱们的思路应该是写一个工具类来替咱们获取配置文件的信息,只有返回一个sqlSession实例就能够了。所以就有了MyBatisUtils.class,上面这样的形式,只有应用sqlSession=MyBatisUtils.getSqlSession();就能够获取到sqlsession的实例。 public class MyBatisUtils { public SqlSession getSqlSession(){ InputStream is; try { is = Resources.getResourceAsStream("mybatis.xml"); return new SqlSessionFactoryBuilder().build(is).openSession(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }}然而以上的形式并不是最好的,还是会浪费资源,如果sqlsession曾经存在了,这段代码还是会去创立一个新的实例对象。咱们晓得sqlsession没有可批改的属性,是线程平安的,所以咱们须要把它改写成单例模式。 ...

November 28, 2020 · 2 min · jiezi

关于mybatis:Mybatis21-从读取流到创建SqlSession发生了什么

[TOC] 咱们应用sqlSession之前,须要去获取配置文件,获取InputStream输出流,通过SqlSessionFactoryBuilder获取sqlSessionFactory对象,从而获取sqlSession。 InputStream is = Resources.getResourceAsStream("mybatis.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);SqlSession sqlSession = sqlSessionFactory.openSession();1.Resources.getResourceAsStream("mybatis.xml")到底做了什么?1.首先咱们来看InputStream is = Resources.getResourceAsStream("mybatis.xml");这句话到底替咱们干了什么,上面能够看出在外面调用了另一个外部办法,resource是全局配置的文件名: public static InputStream getResourceAsStream(String resource) throws IOException { // 从这里字面意思是传一个空的类加载器进去,还有全局配置文件名,从办法名的意思就是 // 将配置文件读取,转化成输出流 return getResourceAsStream((ClassLoader)null, resource); }2.跟进办法中,咱们能够晓得在外面调用ClassLoaderWrapper类的一个实例对象的getResourceAsStream()办法,这个classLoaderWrapper怎么来的呢?这个是Resources.class的一个成员属性,那么这个ClassLoaderWrapper是什么货色呢? 在Resources.class中咱们只是应用private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();创立一个classLoaderWrapper对象。ClassLoaderWrapper其实是一个ClassLoader(类加载器)的包装类,其中蕴含了几个ClassLoader对象,一个defaultClassLoader,一个systemClassLoader,通过外部管制,能够确保返回正确的类加载器给零碎应用。咱们能够当成一个mybatis自定义过的类加载器。 public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); if (in == null) { throw new IOException("Could not find resource " + resource); } else { return in; } }3.咱们能够看出调用了上面这个外部办法,外面调用了封装的办法,一个是获取以后的类加载器,另一个是传进来的文件名: ...

November 28, 2020 · 3 min · jiezi

关于mybatis:mybatis中jdbcType的作用

背景见共事代码中应用到jdbcType,很是纳闷。 常识这是为了程序的安全性,使一些非凡状况,当传入的参数为name为空时不会使程序呈现问题,当name为空时,mybatis不晓得具体要转换成什么jdbcType类型,有些非凡状况会报错,Mybatis经常出现的:有效的列类型: 1111 谬误,就是因为没有设置JdbcType造成的。在Mybatis中,也明文倡议在映射字段数据时须要将JdbcType属性加上。这样相对来说是比拟平安的。 以下状况是在保障了前四种是不能为空的前提下,而前面几项为空时也不至于程序报错。 `<insert id="saveRole"> insert into role_p values ( #{roleId}, #{name}, #{remarks}, #{orderNo}, #{createBy,jdbcType=VARCHAR}, #{createDept,jdbcType=VARCHAR}, #{createTime,jdbcType=DATE}, #{updateBy,jdbcType=VARCHAR}, #{updateTime,jdbcType=DATE})</insert>`常见的类型对应关系如下: JDBC Type Java TypeCHAR StringVARCHAR StringLONGVARCHAR StringNUMERIC java.math.BigDecimalDECIMAL java.math.BigDecimalBIT booleanBOOLEAN booleanTINYINT byteSMALLINT shortINTEGER INTEGERBIGINT longREAL floatFLOAT doubleDOUBLE doubleBINARY byte[]VARBINARY byte[]LONGVARBINARY byte[]DATE java.sql.DateTIME java.sql.TimeTIMESTAMP java.sql.TimestampCLOB ClobBLOB BlobARRAY ArrayDISTINCT mapping of underlying typeSTRUCT StructREF RefDATALINK java.net.URLJdbcType类型和Java类型的对应关系(java.sql.date,java.sql.time,java.sql,Timestamp)https://blog.csdn.net/fanfan1... 参考http://www.mybatis.cn/archive...https://www.cnblogs.com/lzjdm...

November 27, 2020 · 1 min · jiezi

关于mybatis:Spring-Bootmybatis条件查询带下划线字段时返回为null

问题形容MyBatis无奈查问出属性名和数据库字段名不完全相同的数据。 即:属性名和数据库字段名别离为驼峰命名和下划线命名时查出的数据为NULL。 问题剖析MyBatis默认是属性名和数据库字段名一一对应的,即  数据库表列:user_name 实体类属性:user_name 然而java中个别应用驼峰命名  数据库表列:user_name 实体类属性:userName 解决方案在Spring Boot中,能够通过设置map-underscore-to-camel-case属性为true来开启驼峰性能。   MyBatis配置:  application.properties中: 1. #开启驼峰命名转换2. mybatis.configuration.map-underscore-to-camel-case=true application.yml中:  1. mybatis:2.   configuration:3.     map-underscore-to-camel-case: true 应用该配置能够让MyBatis主动将SQL中查出来的带下划线命名的字段,主动转换为驼峰命名,再去匹配类中的属性。 

November 24, 2020 · 1 min · jiezi

关于mybatis:支付宝二面Mybatis接口Mapper内的方法为啥不能重载吗我直接懵逼了

动静代理的性能:通过拦截器办法回调,对指标target办法进行加强。 话中有话就是为了加强指标target办法。下面这句话没错,但也不要认为它就是真谛,殊不知,动静代理还有投鞭断流的霸权,连指标target都不要的科幻模式。注:本文默认认为,读者对动静代理的原理是了解的,如果不明确target的含意,难以看懂本篇文章,倡议先了解动静代理。 1.自定义JDK动静代理之投鞭断流实现主动映射器Mapper 首先定义一个pojo。 public class User { private Integer id; private String name; private int age; public User(Integer id, String name, int age) { this.id = id; this.name = name; this.age = age; } // getter setter} 再定义一个接口UserMapper.java。 public interface UserMapper { public User getUserById(Integer id); } 接下来咱们看看如何应用动静代理之投鞭断流,实现实例化接口并调用接口办法返回数据的。 自定义一个InvocationHandler。 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class MapperProxy implements InvocationHandler { @SuppressWarnings("unchecked") public <T> T newInstance(Class<T> clz) { return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[] { clz }, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { // 诸如hashCode()、toString()、equals()等办法,将target指向以后对象this return method.invoke(this, args); } catch (Throwable t) { } } // 投鞭断流 return new User((Integer) args[0], "zhangsan", 18); }} 下面代码中的target,在执行Object.java内的办法时,target被指向了this,target曾经变成了傀儡、象征、占位符。在投鞭断流式的拦挡时,曾经没有了target。写一个测试代码: ...

November 12, 2020 · 2 min · jiezi

关于mybatis:MyBatis查询Error-instantiating-class-types-or-values

在学习mybatis时的关联关系时遇到了这个问题。 Error instantiating class com.springmvc_mybatis.pojo.Items with invalid types () or values ().实例化具备有效类型()或值()的类com.springmvc_mybatis.pojo.items时出错 解决办法:实例化失败,阐明pojo类的构造函数有问题,本次谬误地点在于我的实体类上,没有提供一个mybatis的resultMap后果集构造函数,我将此构造函数加上就不报错了。因为mapper.xml中有一个resultMap须要用到这个构造函数: <?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.star.mapper.NewsMapper"> <resultMap id="BaseResultMap" type="com.star.model.News"> <constructor> <idArg column="news_id" jdbcType="INTEGER" javaType="java.lang.Integer"/> <arg column="title" jdbcType="VARCHAR" javaType="java.lang.String"/> </constructor> </resultMap> <!-- 新闻对应多个类型标签:一对多关系 --> <resultMap id="newsManyToMany" type="com.star.model.News" extends="BaseResultMap"><!-- <id column="news_id" property="newsId"/>--><!-- <result column="title" property="title"/>--> <collection property="categories" column="newsId" ofType="com.star.model.Category" select="com.star.mapper.CategoryMapper.queryNewsByCategoryId"> </collection> </resultMap> <sql id="Base_Column_List"> news_id, title </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer"> select <include refid="Base_Column_List"/> from t_news where news_id = #{newsId,jdbcType=INTEGER} </select> <select id="queryCategoryByNewId" resultMap="newsManyToMany"> select * from t_news nws left join t_news_category nct on nws.news_id = nct.nid left join t_category ctg on ctg.category_id = nct.cid where nws.news_id = #{newsId} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from t_news where news_id = #{newsId,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="com.star.model.News"> insert into t_news (news_id, title) values (#{newsId,jdbcType=INTEGER}, #{title,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="com.star.model.News"> insert into t_news <trim prefix="(" suffix=")" suffixOverrides=","> <if test="newsId != null"> news_id, </if> <if test="title != null"> title, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="newsId != null"> #{newsId,jdbcType=INTEGER}, </if> <if test="title != null"> #{title,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.star.model.News"> update t_news <set> <if test="title != null"> title = #{title,jdbcType=VARCHAR}, </if> </set> where news_id = #{newsId,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="com.star.model.News"> update t_news set title = #{title,jdbcType=VARCHAR} where news_id = #{newsId,jdbcType=INTEGER} </update></mapper>

November 11, 2020 · 1 min · jiezi

关于mybatis:SpringBoot强化篇三MyBatis框架的整合及原理分析

概述Mybatis是一个优良的长久层框架,底层基于JDBC实现与数据库的交互。并在JDBC操作的根底上做了封装和优化,它借助灵便的SQL定制,参数及后果集的映射形式,更好的适应了以后互联网技术的倒退。Mybatis框架的简略利用架构如图所示:在当今的互联网利用中我的项目,mybatis框架通常会由spring框架进行资源整合,作为数据层技术实现数据交互操作。 筹备工作第一步:创立我的项目module第二步:增加依赖 <dependencies> <!--spring jdbc--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency></dependencies>第三步:application.properties 配置文件中增加繁难配置 #spring datasourcespring.datasource.url=jdbc:mysql:///dbgoods?serverTimezone=GMT%2B8&characterEncoding=utf8spring.datasource.username=rootspring.datasource.password=1234#spring mybatismybatis.mapper-locations=classpath:/mapper/*/*.xml#spring logslogging.file.path=D:/logs/logging.level.com.cy=debug测试Mybatis底层环境在SpringBoot脚手架工程中,Spring框架会基于MyBatis框架底层配置,创立SqlSessionFactory对象,而后再通过此工厂对象创立SqlSession,最初基于Springku框架为测试类注入SqlSession对象,接下来,咱们能够通过SqlSession对象实现与数据库的会话了。 @SpringBootTestpublic class MyBatisTests { /** * SqlSession 此对象为mybatis框架中实现与数据库进行会话 */ @Autowired private SqlSession sqlSession; @Test void testGetConnection(){ Connection conn=sqlSession.getConnection();//从哪里取到的连贯? System.out.println("conn="+conn);//conn=HikariProxyConnection@604990529 wrapping com.zaxxer.hikari.pool.ProxyConnection.ClosedConnection Assertions.assertNotNull(conn);//断言测试法:企业罕用, // 判断对象是不是空,不为空测试通过,否则抛异样 }}MyBatis业务代码实现及原理剖析业务形容基于SpringBoot脚手架工程对MyBatis框架的整合,实现对商品库中商品数据的查问业务。 API架构设计 业务时序图剖析 业务代码设计及实现POJO实体类 ...

November 9, 2020 · 2 min · jiezi

关于mybatis:mybatisPlus-拦截器执行源码

通过 MyBatis 提供的弱小机制,应用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦挡的办法签名即可。 @Slf4j@Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} ), @Signature( type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class} ), @Signature( type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class} ), @Signature( type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class} ),})@Component@Order(-2)public class CustomizeInterceptor implements Interceptor { @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public Object intercept(Invocation invocation) throws Throwable { //获取代理对象 log.info("拦挡:{}", invocation.getMethod().getName()); return invocation.proceed(); }}MybatisConfiguration 中源码解析办法 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new MybatisBatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new MybatisReuseExecutor(this, transaction); } else { executor = new MybatisSimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new MybatisCachingExecutor(executor); } //插件拦截器链 executor = (Executor) interceptorChain.pluginAll(executor); return executor; } } InterceptorChain 中拦截器链条 private final List<Interceptor> interceptors = new ArrayList<>(); public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { //调用 target = interceptor.plugin(target); } return target; } //调用接口 default Object plugin(Object target) { return Plugin.wrap(target, this); }3.它又持续调用了Plugin的静态方法wrap ...

November 5, 2020 · 2 min · jiezi

关于mybatis:SSM基础篇三MyBatis

MyBatisMyBatis 本是apache的一个开源我的项目iBatis,2010年这个我的项目由apache software foundation 迁徙到了google code,并且改名为MyBatis 。2013年11月迁徙到Github。MyBatis是一个优良的长久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只须要关注SQL自身,而不须要破费精力去解决例如注册驱动、创立connection、创立statement、手动设置参数、后果集检索等jdbc繁冗的过程代码。Mybatis通过xml或注解的形式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最初由mybatis框架执行sql并将后果映射成java对象并返回。总之,Mybatis对JDBC拜访数据库的过程进行了封装,简化了JDBC代码,解决JDBC将后果集封装为Java对象的麻烦。(1)mybatis-config.xml是Mybatis的外围配置文件,通过其中的配置能够生成SqlSessionFactory,也就是SqlSession工厂(2)基于SqlSessionFactory能够生成SqlSession对象(3)SqlSession是一个能够发送SQL去执行,并返回后果,相似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。(4)Executor是SqlSession底层的对象,用于执行SQL语句(5)MapperStatement对象也是SqlSession底层的对象,用于接管输出映射(SQL语句中的参数),以及做输入映射(行将SQL查问的后果映射成相应的后果) MyBatis劣势比照1、应用传统形式JDBC拜访数据库:应用JDBC拜访数据库有大量反复代码(比方注册驱动、获取连贯、获取传输器、开释资源等);JDBC本身没有连接池,会频繁的创立连贯和敞开连贯,效率低;SQL是写死在程序中,一旦批改SQL,须要对类从新编译;对查问SQL执行后返回的ResultSet对象,须要手动解决,有时会特地麻烦;2、应用mybatis框架拜访数据库:Mybatis对JDBC对了封装,能够简化JDBC代码;Mybatis本身反对连接池(也能够配置其余的连接池),因而能够进步程序的效率;Mybatis是将SQL配置在mapper文件中,批改SQL只是批改配置文件,类不须要从新编译。对查问SQL执行后返回的ResultSet对象,Mybatis会帮咱们解决,转换成Java对象。mybatis-config.xmlMyBatis的全局配置文件,配置连贯的数据库并导入Mapper配置文件 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><!-- MyBatis的全局配置文件 --><configuration> <!-- 1.配置环境,可配置多个环境(比方:develop开发、test测试) --> <environments default="develop"> <environment id="develop"> <!-- 1.1.配置事务管理形式:JDBC/MANAGED JDBC:将事务交给JDBC治理(举荐) MANAGED:本人治理事务 --> <transactionManager type="JDBC"></transactionManager> <!-- 1.2.配置数据源,即连接池 JNDI/POOLED/UNPOOLED JNDI:已过期 POOLED:应用连接池(举荐) UNPOOLED:不应用连接池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <!-- 2.导入Mapper配置文件,如果mapper文件有多个,能够通过多个mapper标签导入 --> <mappers> <mapper resource="EmpMapper.xml" /> </mappers></configuration>映射关系实体类Emppackage com.tedu.pojo;public class Emp { // 1.申明实体类中的属性 private Integer id; private String name; private String job; private Double salary; // 2.提供对应的getter和setter办法 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } // 3.重写toString办法 @Override public String toString() { return "Emp [id=" + id + ", name=" + name + ", job=" + job + ", salary=" + salary + "]"; }}接口EmpMapperpackage com.tedu.dao;import com.tedu.pojo.Emp;public interface EmpMapper { /** * 依据id查问员工信息 * @param id * @return Emp */ public Emp findById(Integer id);}映射文件EmpMapper.xml<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- 不同Mapper文件的namespace值应该保障惟一 在程序中通过[ namespace + id ]定位到要执行哪一条SQL语句 --><mapper namespace="com.tedu.dao.EmpMapper"> <!-- 通过select、insert、update、delete标签申明要执行的SQL --> <!-- 1.查问:查问Emp表中指定id的员工信息 --> <select id="findById" resultType="com.tedu.pojo.Emp"> select * from emp where id=#{id} </select></mapper>映射文件标签阐明后面是xml的文档申明,用于申明xml的版本和编码,引入了xml束缚文档,以后xml文档将会依照mybatis-3-mapper.dtd文件所要求的规定进行书写。 Mapper标签:根标签,其中namespace(名称空间,也叫命名空间),要求不能反复。在程序中通过【namespace + id 】定位到要执行哪一条SQL语句select标签:用于指定未来要执行的各种SQL语句。标签上能够申明属性,上面介绍罕用的属性:id、resultType、resultMapid属性:要求值不能反复。未来在执行SQL时,能够通过【namespace +id】找到指定SQL并执行。resultType属性:从这条SQL语句中返回所冀望类型的类的齐全限定名称(包名+类名)。留神如果是汇合情景,那应该是汇合能够蕴含的类型,而不能是汇合自身。简而言之,resultType管制查问SQL执行后返回值的类型或汇合中的泛型,例如查问emp表中的单条记录,返回值是一个Emp对象,因而,resultType="com.tedu.pojo.Emp";如果查问emp表中的多条记录,返回值是一个List,此时resultType的值应该汇合中的泛型,因而resultType="com.tedu.pojo.Emp";resultMap属性:简单对象构造(例如多表关联查问等)。 应用 resultType或 resultMap,但不能同时应用。 ...

November 1, 2020 · 2 min · jiezi

关于mybatis:开发工具MybatisPlus插件三种方式的逆向工程

本文源码:GitHub·点这里 || GitEE·点这里 一、逆向工程简介在Java开发中,长久层最罕用的框架就是mybatis,该框架须要编写sql语句,mybatis官网提供逆向工程,能够把数据表主动生成执行所须要的根底代码,例如:mapper接口,sql映射文件,pojo实体类等,防止根底代码保护的繁冗过程。 在理论的应用中,罕用的逆向工程形式如上,mybatis框架,mybatis-plus框架,插件形式。 二、Mybatis形式1、根底形容基于xml配置的形式,生成mybatis根底代码,包含mapper接口,Mapper映射文件,pojo实体类,PojoExample条件工具类。 2、配置文件留神这里的targetProject须要配置自定义门路地位。 <?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"> <commentGenerator> <!-- 是否去除主动生成的正文 true:是 : false:否 --> <property name="suppressAllComments" value="true"/> <property name="suppressDate" value="false"/> <!-- 是否增加数据表中字段的正文 true:是 : false:否 --> <property name="addRemarkComments" value="true"/> </commentGenerator> <!--数据库的信息:驱动类、连贯地址、用户名、明码 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/defined-log?tinyInt1isBit=false" userId="root" password="123456"> </jdbcConnection> <!-- 默认false,把JDBC decimal 和 numeric 类型解析为 Integer 设置true时把JDBC decimal 和 numeric 类型解析为BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- 生成POJO类的地位 --> <javaModelGenerator targetPackage="com.generator.mybatis.pojo" targetProject="寄存门路"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- 生成Mapper映射文件的地位 --> <sqlMapGenerator targetPackage="com.generator.mybatis.xml" targetProject="寄存门路"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <!-- 生成Mapper接口的地位 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.generator.mybatis.mapper" targetProject="寄存门路"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!-- 指定数据库表 --> <table schema="" tableName="dt_defined_log" domainObjectName="DefinedLog"/> </context></generatorConfiguration>3、启动类读取配置文件,并执行。 ...

November 1, 2020 · 2 min · jiezi

关于mybatis:mybatis-百万数据插入

开启批量插入数据库连贯url中减少一个rewriteBatchedStatements=true(开启批量插入)2.用foreach拼接sql插入语句 环境配置环境阐明:WIN10+16G内存+i7-4770U3.40GHz+JDK8+MySQL8.0+上代码 @Test public void monitor(){ //循环次数 int count = 100; //每次插入条数 int size = 10000; Calendar calendar = Calendar.getInstance(); //calendar的月从0开始算 calendar.set(2011,05,01,0,0,0); long dayTime = calendar.getTimeInMillis(); long sumTime = 0L; for (int i = 0; i < count; i++) { List<Monitor> monitors = new ArrayList<Monitor>(); for (int j = 0; j < size; j++) { Monitor monitor = new Monitor(); monitor.setId(IdWorker.getId()); monitor.setBdjm("铸造二期3#进线"); monitor.setBdz("00000000023"); monitor.setRqsj(new Date(dayTime)); dayTime += 300_000L; // 5*60*1000为5分钟 monitor.setYgdl(RandomUtil.getRandomNumber(1,10)); monitor.setWgdl(RandomUtil.getRandomNumber(1,10)); monitor.setFwgdl(RandomUtil.getRandomNumber(1,10)); monitor.setFygdl(RandomUtil.getRandomNumber(1,10)); monitor.setOnexxwg(RandomUtil.getRandomNumber(1,10)); monitor.setThreexxwg(RandomUtil.getRandomNumber(1,10)); monitor.setTwoxxwg(RandomUtil.getRandomNumber(1,10)); monitor.setFourxxwg(RandomUtil.getRandomNumber(1,10)); monitor.setAa(RandomUtil.getRandomNumber(1,10)); monitor.setAv(RandomUtil.getRandomNumber(1,10)); monitor.setBa(RandomUtil.getRandomNumber(1,10)); monitor.setBv(RandomUtil.getRandomNumber(1,10)); monitor.setCa(RandomUtil.getRandomNumber(1,10)); monitor.setCv(RandomUtil.getRandomNumber(1,10)); monitor.setGlcs(RandomUtil.getRandomNumber(1,10)); monitor.setSzgl(RandomUtil.getRandomNumber(1,10)); monitor.setWggl(RandomUtil.getRandomNumber(1,10)); monitor.setAxgl(RandomUtil.getRandomNumber(1,10)); monitor.setAxglcs(RandomUtil.getRandomNumber(1,10)); monitor.setAxwgl(RandomUtil.getRandomNumber(1,10)); monitor.setBxgl(RandomUtil.getRandomNumber(1,10)); monitor.setBxglcs(RandomUtil.getRandomNumber(1,10)); monitor.setBxwgl(RandomUtil.getRandomNumber(1,10)); monitor.setCxgl(RandomUtil.getRandomNumber(1,10)); monitor.setCxglcs(RandomUtil.getRandomNumber(1,10)); monitor.setCxwgl(RandomUtil.getRandomNumber(1,10)); monitor.setYggl(RandomUtil.getRandomNumber(1,10)); monitor.setDwpl(RandomUtil.getRandomNumber(1,10)); monitor.setUabUaAngle(RandomUtil.getRandomNumber(1,10)); monitor.setUbAngle(RandomUtil.getRandomNumber(1,10)); monitor.setUcbUcAngle(RandomUtil.getRandomNumber(1,10)); monitor.setZglcs(RandomUtil.getRandomNumber(1,10)); monitor.setDlbph(RandomUtil.getRandomNumber(1,10)); monitor.setIaAngle(RandomUtil.getRandomNumber(1,10)); monitor.setIbAngle(RandomUtil.getRandomNumber(1,10)); monitor.setIcAngle(RandomUtil.getRandomNumber(1,10)); monitors.add(monitor); } Instant inst1 = Instant.now(); monitorService.batchInsert(monitors); Instant inst2 = Instant.now(); sumTime += ChronoUnit.MILLIS.between(inst1, inst2); } System.out.println( formatTime(sumTime) );}public static String formatTime(Long ms) { Integer ss = 1000; Integer mi = ss * 60; Integer hh = mi * 60; Integer dd = hh * 24; Long day = ms / dd; Long hour = (ms - day * dd) / hh; Long minute = (ms - day * dd - hour * hh) / mi; Long second = (ms - day * dd - hour * hh - minute * mi) / ss; Long milliSecond = ms - day * dd - hour * hh - minute * mi - second * ss; StringBuffer sb = new StringBuffer(); if(day > 0) { sb.append(day+"天"); } if(hour > 0) { sb.append(hour+"小时"); } if(minute > 0) { sb.append(minute+"分"); } if(second > 0) { sb.append(second+"秒"); } if(milliSecond > 0) { sb.append(milliSecond+"毫秒"); } return sb.toString();}

October 24, 2020 · 2 min · jiezi

关于mybatis:Mybatis-select记录封装

select记录封装返回一个List汇合, resultType要写汇合中元素的类型<!-- public List<Employee> getEmpsByLastNameLike(String lastName); --><!--resultType:如果返回的是一个汇合,要写汇合中元素的类型 --><select id="getEmpsByLastNameLike" resultType="com.atguigu.mybatis.bean.Employee"> select * from tbl_employee where last_name like #{lastName}</select>返回一条记录的map, key为列名, 值就是对应的值<!--public Map<String, Object> getEmpByIdReturnMap(Integer id); --><select id="getEmpByIdReturnMap" resultType="map"> select * from tbl_employee where id=#{id}</select>多条记录封装成一个map, key为id, 值是记录封装后的javaBean//@MapKey:通知mybatis封装这个map的时候应用哪个属性作为map的key@MapKey("lastName")public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);<!--public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName); --><select id="getEmpByLastNameLikeReturnMap" resultType="com.atguigu.mybatis.bean.Employee"> select * from tbl_employee where last_name like #{lastName}</select>主动映射配置全局setting设置autoMappingBehavior默认为PARTIAL, 开启主动映射性能;惟一的要求是列名和javaBean属性名统一2.mapUnderscoreToCamelCase=true, 开启主动驼峰命名标准映射性能 自定义resultMap, 实现高级映射性能resultMap自定义映射规定 <!--自定义某个javaBean的封装规定 type:自定义规定的Java类型 id:惟一id不便援用 --> <resultMap type="com.atguigu.mybatis.bean.Employee" id="MySimpleEmp"> <!--指定主键列的封装规定 id定义主键会底层有优化; column:指定哪一列 property:指定对应的javaBean属性 --> <id column="id" property="id"/> <!-- 定义一般列封装规定 --> <result column="last_name" property="lastName"/> <!-- 其余不指定的列会主动封装:咱们只有写resultMap就把全副的映射规定都写上。 --> <result column="email" property="email"/> <result column="gender" property="gender"/> </resultMap>创立表create table tb_dept (id int(11) primary key auto_increment,dept_name varchar(255))增加列 ...

October 21, 2020 · 3 min · jiezi

关于mybatis:mybatis中的和的区别

mybatis中的#和$的区别: 1、#将传入的数据都当成一个字符串,会对主动传入的数据加一个双引号。如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username="111", 如果传入的值是id,则解析成的sql为where username="id". 2、$将传入的数据间接显示生成在sql中。如:where username=${username},如果传入的值是111,那么解析成sql时的值为where username=111; 3、针对下面的sql,如果传入的值是;drop table user;, 那么第一条用#{}的sql解析为:select id, username, password, role from user where username=";drop table user;" 那么第二条用${}的sql解析为:select id, username, password, role from user where username=;drop table user; 这时候曾经sql注入了。 3、#形式可能很大水平避免sql注入,$形式无奈避免Sql注入。4、$形式个别用于传入数据库对象,例如传入表名和列名,还有排序时应用order by动静参数时须要应用$ ,ORDER BY ${columnName}5、个别能用#的就别用$,若不得不应用“${xxx}”这样的参数,要手工地做好过滤工作,来避免sql注入攻打。6、在MyBatis中,“${xxx}”这样格局的参数会直接参与SQL编译,从而不能防止注入攻打。但波及到动静表名和列名时,只能应用“${xxx}”这样的参数格局。所以,这样的参数须要咱们在代码中手工进行解决来避免注入。【论断】在编写MyBatis的映射语句时,尽量采纳“#{xxx}”这样的格局。若不得不应用“${xxx}”这样的参数,要手工地做好过滤工作,来避免SQL注入攻打。

October 19, 2020 · 1 min · jiezi

关于mybatis:mybatis框架的理解

什么是mybaits?MyBatis 本是apache的一个开源我的项目iBatis,2010年这个我的项目由apache software foundation 迁徙到了google code,并且改名为MyBatis 。2013年11月迁徙到Github。MyBatis是一个优良的长久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只须要关注SQL自身,而不须要破费精力去解决例如注册驱动、创立connection、创立statement、手动设置参数、后果集检索等jdbc繁冗的过程代码。Mybatis通过xml或注解的形式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最初由mybatis框架执行sql并将后果映射成java对象并返回。总之,Mybatis对JDBC拜访数据库的过程进行了封装,简化了JDBC代码,解决JDBC将后果集封装为Java对象的麻烦。以上是mybatis的官网解释,上面是我集体的了解(不对的请评论区留言哦)。所谓mybatis就是底层封装了jdbc简化了代码利用框架来连贯数据库并对其进行操作。 mybatis的配置文件各个标签的含意<configuration ></onfiguration > : MyBatis的全局配置文件 evironments 标签:该标签外部能够配置多个environment,即多种环境,每种环境能够做不同配置或连贯不同数据库。例如,开发、测试、生产环境可能须要不同的配置,连贯的数据库可能也不雷同,因而咱们能够配置三个environment,别离对应下面三种不同的环境。 然而要记住,environment能够配置多个,然而最终要应用的只能是其中一个!environment 标签:外部能够配置多种配置信息,上面介绍事务管理配置和数据源配置。 transactionManage 标签:事务管理配置,mybatis中有两种事务管理形式,也就是type="[JDBC|MANAGED] dataSource标签:数据源,也就是连接池配置。这里type指定数据源类型,有三种内建的类型:JNDI、POOLED、UNPOOLED mappers标签:用于导入mapper文件的地位,其中能够配置多个mapper,即能够导入多个mapper文件。 Mapper的配置的文件标签含意: (1)第1行是xml的文档申明,用于申明xml的版本和编码 (2)第2、3、4行,引入了xml束缚文档,以后xml文档将会依照mybatis-3-mapper.dtd文件所要求的规定进行书写。 (3)Mapper标签:根标签,其中namespace(名称空间,也叫命名空间),要求不能反复。在程序中通过【namespace + id 】定位到要执行哪一条SQL语句 (4)select标签:用于指定未来要执行的各种SQL语句。标签上能够申明属性,上面介绍罕用的属性:id、resultType、resultMap id属性:要求值不能反复。未来在执行SQL时,能够通过【namespace + id】找到指定SQL并执行。resultType属性:从这条SQL语句中返回所冀望类型的类的齐全限定名称(包名+类名)。留神如果是汇合情景,那应该是汇合能够蕴含的类型,而不能是汇合自身。简而言之,resultType管制查问SQL执行后返回值的类型或汇合中的泛型,例如查问emp表中的单条记录,返回值是一个Emp对象,因而,resultType="com.tedu.pojo.Emp"; 如果查问emp表中的多条记录,返回值是一个List<Emp>,此时resultType的值应该汇合中的泛型,因而resultType="com.tedu.pojo.Emp"; resultMap属性:简单对象构造(例如多表关联查问等)。 应用 resultType 或 resultMap,但不能同时应用

October 15, 2020 · 1 min · jiezi

关于mybatis:第六阶段-第一模块

namespace 首先找到对应的接口mapper id 找到对应的办法参数互相对应返回值互相对应 id反复了起别名 不能用uid 了因为有null

October 3, 2020 · 1 min · jiezi

关于mybatis:mybatis源码分析

一、简略阐明springboot与mybatis的集成1.springboot外围就是主动加载配置,次要是通过@EnableAutoConfiguration注解 EnableAutoConfiguration次要是通过AutoConfigurationImportSelector来加载,通过反射加载spring.factories中指定的java类。Mybatis引入的主动配置jar包,spring.factories里配置的是MybatisAutoConfiguration类,则springboot启动时,会主动加载MybatisAutoConfiguration,初始化时,会加载mybatis的配置文件,创立sqlSessionFactory等二、mybatis执行过程剖析首选上一段代码,执行整个过程,加载mybatis配置文件,创立sqlSessionFactory等,获取mapper的代理类,以及执行对应的办法 try { //读取配置文件 Reader reader = Resources.getResourceAsReader("com/mat/example/resource/mybatis-config.xml"); //依据mybatis-config配置文件,生成对应的sqlSessionFactory SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); //通过sessionFactory创立sqlSession,每次操作数据库都是一个sqlSession SqlSession sqlSession = sessionFactory.openSession(); //获取UserMapper的代理类,代理类外面包含userMapper.xml的所有办法,其本质就是创立jdbc连贯执行sql,返回数据 UserMapper mapper = sqlSession.getMapper(UserMapper.class); //执行对应的办法 User user = mapper.getUser(1); System.out.println(user.getUsername()); } catch (IOException e) { e.printStackTrace(); }下面就是mybatis执行的整个过程: 三、mybatis代码加载执行过程剖析 外围就是创立SqlSessionFactory过程,该类有个外部类Configuration,mybatis所有的配置以及mapper.xml文件的配置信息,都会在Configuration外面存着,所有的一切都是为了创立Configuration上面咱们来一步一步剖析,外面有什么货色

September 29, 2020 · 1 min · jiezi

关于mybatis:面试面试官有没有在Mybatis执行过程上为过难你呢看完就不再怂图文解析

前言在理解了MyBatis初始化加载过程后,咱们也应该钻研看看SQL执行过程是怎么执行?这样咱们对于Mybatis的整个执行流程都相熟了,在开发遇到问题也能够很快定位到问题。 更重要的,在面试中遇到面试官征询Mybatis的知识点的时候,能够很顺畅的把这一套流程讲进去,面试官也会感觉你已把握Mybatis知识点了,可能就不问了。连忙瞄瞄 简介SQL执行过程通过MyBatis初始化加载Sql执行过程所需的信息后,咱们就能够通过 SqlSessionFactory 对象失去 SqlSession ,而后执行 SQL 语句了,接下来看看Sql执行具体过程,SQL大抵执行流程图如下所示: 接下来咱们来看看每个执行链路中的具体执行过程, SqlSessionSqlSession 是 MyBatis 裸露给内部应用的对立接口层,通过 SqlSessionFactory 创立,且其是蕴含和数据库打交道所有操作接口。 上面通过时序图形容 SqlSession 对象的创立流程: 在生成SqlSession的同时,基于executorType初始化好Executor 实现类。 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }最顶层的SqlSession接口已生成,那咱们能够来看看sql的执行过程下一步是怎么的呢?怎么应用代理类MapperProxy。 ...

September 27, 2020 · 6 min · jiezi

关于mybatis:SpringBoot-上PageHelper的注意事项

pom.xml配置 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>*.*.*</version> </dependency>bean注入,这里能够自定义配置@Configurationpublic class PageHelperConfig { @Bean(name = "pageHelper") public PageHelper pageHelper() { PageHelper pageHelper = new PageHelper(); Properties properties = new Properties(); /**默认false,设置为true时,会将RowBounds第一个参数offset当成pageNum页码应用*/ properties.setProperty("offsetAsPageNum", "true"); /**默认false,设置为true时,应用RowBounds分页会进行count查问 */ properties.setProperty("rowBoundsWithCount", "true"); /** 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 */ properties.setProperty("reasonable", "false"); /** always总是返回PageInfo类型,check查看返回类型是否为PageInfo,none返回Page */ properties.setProperty("returnPageInfo", "check"); /** 反对通过Mapper接口参数来传递分页参数 */ properties.setProperty("supportMethodsArguments", "true"); /** 配置数据库的方言 */ properties.setProperty("dialect", "mysql"); pageHelper.setProperties(properties); return pageHelper; }}在要分页的中央增加PageHelper.startPage(pageNum,pageSize); List<Blog> blogList = blogMapper.selectByExample(new BlogExample());

September 23, 2020 · 1 min · jiezi

关于mybatis:mybatisorgapacheibatisexecutorExecutorException

在应用mybatis进行一对多嵌套查问时, 报错如下 org.apache.ibatis.exceptions.PersistenceException: Error querying database. Cause: org.apache.ibatis.executor.ExecutorException: Statement returned more than one row, where no more than one was expected.The error may exist in com/bigdata/mapper/UserMapper.xmlThe error may involve com.bigdata.mapper.UserMapper.findAllOrdersThe error occurred while handling resultsSQL: select * from orders where uid = ?Cause: org.apache.ibatis.executor.ExecutorException: Statement returned more than one row, where no more than one was expected.谬误波及代码如下 谬误大体是说, 箭头指向的sql语句返回的数据行数大于1.这个在业务上是OK的, 因为orders表是订单表, 一个用户可能有多个订单.然而在这里后果多行这件事被作为谬误看待, 阐明应该是哪里没有告知mybatis应该应用汇合(或数组)承接后果 最终查找到问题在<collection property="">, property属性值在com.bigdata.domain.User的定义中是Orders, 而不是能够进行遍历的List类型 所以解决形式也很显著了, 将该成员变量改为可迭代类型, 将相应地批改get/set办法以及toString()办法即可 ...

September 22, 2020 · 1 min · jiezi

关于mybatis:mybatisjavalangIllegalArgumentException

在应用mybatis一对一的嵌套查问时, 报错如下: org.apache.ibatis.exceptions.PersistenceException: Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.bigdata.mapper.UserMapper.findById The error may exist in com/bigdata/mapper/OrderMapper.xml The error may involve com.bigdata.mapper.OrderMapper.findAllOrders2The error occurred while handling resultsSQL: select * from ordersCause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.bigdata.mapper.UserMapper.findById网上找来找去, 解决办法根本都是查namespace之类的, 查看名称是否统一. 这些谬误我都没犯, 不适用于我这个问题, 最终解决办法如下阐明. 首先, 依赖的包的版本别离是 mysql : 8.0.11mybatis : 3.5.5junit : 4.13接下来, 上述谬误波及到的代码如下 下面报错的粗心是: 找不到findById可是就像我框起来的代码, 在select属性中, 我十分明确地指出了findById所在的门路. ...

September 22, 2020 · 1 min · jiezi

关于mybatis:MyBatis面试题分析导读架构以及核心内容

该系列文章收录在公众号【Ccww技术博客】,原创技术文章早于博客推出前言MyBatis不论在是平时的应用还是在面试中都必须把握一个知识点,MyBatis 是反对自定义 SQL、存储过程和高级映射的类长久框架,跟数据库打交道的一个开源长久化框架 咱们来看看MyBatis架构: MyBatis的整体架构分为三层: 根底反对层外围解决层接口层咱们这边次要解说一下外围解决层组件 外围解决层在外围解决层中,实现了 MyBatis的外围解决流程: MyBatis的初始化以及实现一次数据库操作的波及的全副流程 ,次要模块分为: 配置解析SQL 解析与参数映射SQL 执行与后果集映射插件配置解析配置解析对应模块: builder 和 mapping ,次要组件为: XMLConfigBuilder: 解析mybatis-config.xml 配置文件XMLMapperBuilder:解析映射配置文件Mapper.xmlXMLStatementBuilder:次要负责解析 Statement 配置,即 <select />、<insert />、<update />、<delete /> 标签MapperAnnotationBuilder:负责解析 Mapper 接口上的注解在 Mybatis 初始化过程中,会加载 mybatis-config.xml 配置文件、加载映射配置文件以及 Mapper 接口中的注解@Mapper信息, 经XML解析properties配置ConfigBuilder::parse解析的配置信息会造成相应的对象并保留到 Configration 对象中。之后,依据基于Configration 创立SqlSessionFactory 对象,待 Mybatis 初始化实现后,能够通过 SqlSessionFactory 创立 SqlSession 对象并开始数据库操作。 Configuration: MyBatis 所有的配置信息都维持在 Configuration 对象之中。 SQL 解析与参数映射SQL 解析模块: scripting ,XMLLanguageDriver,XMLScriptBuilder实现了动静 SQL 语句,其提供了多种动静 SQL语句对应的节点。比方: <where> 节点、<set> 节点、<foreach> 节点等 。通过这些节点的组合应用, 简直能够编写出所有满足需要的 SQL。 ...

September 20, 2020 · 2 min · jiezi

关于mybatis:Mybatis中pagehelper分页插件

1.PageHelper依赖查找. 2.分页插件利用步骤示例

September 15, 2020 · 1 min · jiezi

关于mybatis:Mybatis中的一级缓存二级缓存

局部内容mybatis中一级缓存利用地位mybatis中二级缓存利用地位MyBatis中二级缓存readOnly属性剖析 1.当readOnly为true时,sqlSession对象会将查问后果间接存储到cache,在cache中是一个Map汇合,最终后果指向用一个对象,所以result1==result2,地址值比拟为trueresult1.equals(result2) 属性值比拟也为true2当readOnly为false时,会将对象转化为字节(序列化)存储到cache,在cache中是一个Map汇合,接着会进行反序列化操作,所以指向的是不必对象但内容雷同。result1==result2,地址值比拟为falseresult1.equals(result2) 属性值为trueMapper文件中readOnly的增加地位

September 15, 2020 · 1 min · jiezi

关于mybatis:springboot2结合mybatis拦截器实现主键自动生成

前言前阵子和敌人聊天,他说他们我的项目有个需要,要实现主键主动生成,不想每次新增的时候,都手动设置主键。于是我就问他,那你们数据库表设置主键主动递增不就得了。他的答复是他们我的项目目前的id都是采纳雪花算法来生成,因而为了我的项目稳定性,不会切换id的生成形式。 敌人问我有没有什么实现思路,他们公司的orm框架是mybatis,我就倡议他说,不然让你老大把mybatis切换成mybatis-plus。mybatis-plus就反对注解式的id主动生成,而且mybatis-plus只是对mybatis进行加强不做扭转。敌人还是那句话,说为了我的项目稳固,之前项目组没有应用mybatis-plus的教训,贸然切换不晓得会不会有什么坑。前面没招了,我就跟他说不然你用mybatis的拦截器实现一个吧。于是又有一篇吹水的创作题材呈现。 前置常识在介绍如何通过mybatis拦截器实现主键主动生成之前,咱们先来梳理一些知识点 1、mybatis拦截器的作用mybatis拦截器设计的初衷就是为了供用户在某些时候能够实现本人的逻辑而不用去动mybatis固有的逻辑 2、Interceptor拦截器每个自定义拦截器都要实现 org.apache.ibatis.plugin.Interceptor这个接口,并且自定义拦截器类上增加@Intercepts注解 3、拦截器能拦挡哪些类型Executor:拦挡执行器的办法。ParameterHandler:拦挡参数的解决。ResultHandler:拦挡后果集的解决。StatementHandler:拦挡Sql语法构建的解决。4、拦挡的程序a、不同类型拦截器的执行程序 Executor -> ParameterHandler -> StatementHandler -> ResultSetHandlerb、多个拦截器拦挡同种类型同一个指标办法,执行程序是后配置的拦截器先执行 比方在mybatis配置如下 <plugins> <plugin interceptor="com.lybgeek.InterceptorA" /> <plugin interceptor="com.lybgeek.InterceptorB" /> </plugins>则InterceptorB先执行。 如果是和spring做了集成,先注入spring ioc容器的拦截器,则后执行。比方有个mybatisConfig,外面有如下拦截器bean配置 @Bean public InterceptorA interceptorA(){ return new InterceptorA(); } @Bean public InterceptorB interceptorB(){ return new InterceptorB(); }则InterceptorB先执行。当然如果你是间接用@Component注解这模式,则能够配合@Order注解来管制加载程序 5、拦截器注解介绍@Intercepts:标识该类是一个拦截器 @Signature:指明自定义拦截器须要拦挡哪一个类型,哪一个办法。@Signature注解属性中的type示意对应能够拦挡四种类型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的一种;method示意对应类型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的哪类办法;args示意对应method中的参数类型 6、拦截器办法介绍a、 intercept办法 public Object intercept(Invocation invocation) throws Throwable这个办法就是咱们来执行咱们本人想实现的业务逻辑,比方咱们的主键主动生成逻辑就是在这边实现。 Invocation这个类中的成员属性target就是@Signature中的type;method就是@Signature中的method;args就是@Signature中的args参数类型的具体实例对象 b、 plugin办法 public Object plugin(Object target)这个是用返回代理对象或者是原生代理对象,如果你要返回代理对象,则返回值能够设置为 Plugin.wrap(target, this);this为拦截器如果返回是代理对象,则会执行拦截器的业务逻辑,如果间接返回target,就是没有拦截器的业务逻辑。说白了就是通知mybatis是不是要进行拦挡,如果要拦挡,就生成代理对象,不拦挡是生成原生对象 c、 setProperties办法 public void setProperties(Properties properties)用于在Mybatis配置文件中指定一些属性 ...

September 12, 2020 · 3 min · jiezi

关于mybatis:springboot2结合mybatis拦截器实现主键自动生成

前言前阵子和敌人聊天,他说他们我的项目有个需要,要实现主键主动生成,不想每次新增的时候,都手动设置主键。于是我就问他,那你们数据库表设置主键主动递增不就得了。他的答复是他们我的项目目前的id都是采纳雪花算法来生成,因而为了我的项目稳定性,不会切换id的生成形式。 敌人问我有没有什么实现思路,他们公司的orm框架是mybatis,我就倡议他说,不然让你老大把mybatis切换成mybatis-plus。mybatis-plus就反对注解式的id主动生成,而且mybatis-plus只是对mybatis进行加强不做扭转。敌人还是那句话,说为了我的项目稳固,之前项目组没有应用mybatis-plus的教训,贸然切换不晓得会不会有什么坑。前面没招了,我就跟他说不然你用mybatis的拦截器实现一个吧。于是又有一篇吹水的创作题材呈现。 前置常识在介绍如何通过mybatis拦截器实现主键主动生成之前,咱们先来梳理一些知识点 1、mybatis拦截器的作用mybatis拦截器设计的初衷就是为了供用户在某些时候能够实现本人的逻辑而不用去动mybatis固有的逻辑 2、Interceptor拦截器每个自定义拦截器都要实现 org.apache.ibatis.plugin.Interceptor这个接口,并且自定义拦截器类上增加@Intercepts注解 3、拦截器能拦挡哪些类型Executor:拦挡执行器的办法。ParameterHandler:拦挡参数的解决。ResultHandler:拦挡后果集的解决。StatementHandler:拦挡Sql语法构建的解决。4、拦挡的程序a、不同类型拦截器的执行程序 Executor -> ParameterHandler -> StatementHandler -> ResultSetHandlerb、多个拦截器拦挡同种类型同一个指标办法,执行程序是后配置的拦截器先执行 比方在mybatis配置如下 <plugins> <plugin interceptor="com.lybgeek.InterceptorA" /> <plugin interceptor="com.lybgeek.InterceptorB" /> </plugins>则InterceptorB先执行。 如果是和spring做了集成,先注入spring ioc容器的拦截器,则后执行。比方有个mybatisConfig,外面有如下拦截器bean配置 @Bean public InterceptorA interceptorA(){ return new InterceptorA(); } @Bean public InterceptorB interceptorB(){ return new InterceptorB(); }则InterceptorB先执行。当然如果你是间接用@Component注解这模式,则能够配合@Order注解来管制加载程序 5、拦截器注解介绍@Intercepts:标识该类是一个拦截器 @Signature:指明自定义拦截器须要拦挡哪一个类型,哪一个办法。@Signature注解属性中的type示意对应能够拦挡四种类型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的一种;method示意对应类型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的哪类办法;args示意对应method中的参数类型 6、拦截器办法介绍a、 intercept办法 public Object intercept(Invocation invocation) throws Throwable这个办法就是咱们来执行咱们本人想实现的业务逻辑,比方咱们的主键主动生成逻辑就是在这边实现。 Invocation这个类中的成员属性target就是@Signature中的type;method就是@Signature中的method;args就是@Signature中的args参数类型的具体实例对象 b、 plugin办法 public Object plugin(Object target)这个是用返回代理对象或者是原生代理对象,如果你要返回代理对象,则返回值能够设置为 Plugin.wrap(target, this);this为拦截器如果返回是代理对象,则会执行拦截器的业务逻辑,如果间接返回target,就是没有拦截器的业务逻辑。说白了就是通知mybatis是不是要进行拦挡,如果要拦挡,就生成代理对象,不拦挡是生成原生对象 c、 setProperties办法 public void setProperties(Properties properties)用于在Mybatis配置文件中指定一些属性 ...

September 12, 2020 · 3 min · jiezi

关于mybatis:Spring-Boot-整合-Spring-MVC的基本步骤

Spring Boot 整合Spring MVC 过程 关系图:这种关系图的程序代码的书写代码的程序是从左边往左边写!要了解Spring MVC 的操作了解:做这些用到了mybatis 的整合,这样简化了jdbc连贯数据库的操作上面是书写的代码:-- Application.properties close bannerspring.main.banner-mode=off Spring DateSourcespring.datasource.url=jdbc:mysql:///dbgoods?serverTimezone=GMT%2B8&characterEncoding=utf8spring.datasource.username=rootspring.datasource.password=root spring mybatismybatis.mapper-locations=classpath:/mapper/goods/GoodsMapper.xml spring loglogging.level.com.cy=debug serverserver.port=80server.servlet.context-path=/binbin spring thymleafspring.thymeleaf.prefix=classpath:/templates/pages/spring.thymeleaf.suffix=.htmlspring.thymeleaf.cache=false--GoodsMapper.xml<?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.cy.pj.goods.dao.GoodsDao"> <delete id="deleteObjects"> delete from tb_goods where id in <!-- (1,2,3,4,5) --> <foreach collection="ids" open="(" close=")" separator="," item="id"> #{id} </foreach></delete></mapper>Goods.javapackage com.cy.pj.goods.pojo; import java.util.Date; public class Goods { private Long id;//id bigint primary key auto_increment private String name;//name varchar(100) not null private String remark;//remark text private Date createdTime;//createdTime datetimepublic Long getId() { return id;}public void setId(Long id) { this.id = id;}public String getName() { return name;}public void setName(String name) { this.name = name;}public String getRemark() { return remark;}public void setRemark(String remark) { this.remark = remark;}public Date getCreatedTime() { return createdTime;}public void setCreatedTime(Date createdTime) { this.createdTime = createdTime;}@Overridepublic String toString() { return "Goods [id=" + id + ", name=" + name + ", remark=" + remark + ", createdTime=" + createdTime + "]";} }GoodsDao(interface)package com.cy.pj.goods.dao; ...

September 12, 2020 · 4 min · jiezi

关于mybatis:mybatis-一级缓存导致查询结果与预期结果不一致

示例sql: @Transactional public RebateContract getInfo(RebateContract contract) { RebateContract rebContractInfo = rebateContractDao.getInfo(contract); logger.info("agentId:" + rebContractInfo.getAgentId()); rebContractInfo.setAgentId(10000L); // 不会再查问 而是从缓存中获取rebContractInfo的值 RebateContract rebContractInfo1 = rebateContractDao.getInfo(contract); logger.info("agentId1:" + rebContractInfo1.getAgentId()); return rebContractInfo1; }执行后果:留神:必须同一个事物中同一个事务中查问一次后,间接修复查问后后果,再次雷同sql查问,理论不会再查问,而是从缓存中获取上次的查问后果。但上次查问后果曾经被批改,所以前面查问获取到的值已与理论查问该获取的值不一样了。

September 11, 2020 · 1 min · jiezi

关于mybatis:mybatis升级为mybatisplus踩到的坑

前言最近应用RuoYi-Vue来做后盾治理脚手架。RuoYi-Vue 是一个 Java EE 企业级疾速开发平台,基于经典技术组合(Spring Boot、Spring Security、MyBatis、Jwt、Vue),内置模块如:部门治理、角色用户、菜单及按钮受权、数据权限、零碎参数、日志治理、代码生成等。在线定时工作配置;反对集群,反对多数据源。其官网文档如下 http://doc.ruoyi.vip/感兴趣的敌人,能够点链接查看。这个平台目前的orm框架是mybatis,而项目组的orm框架是mybatis-plus。为了对立技术栈,项目组就决定把若依的orm框架降级为mybatis-plus。因为之前就有过把mybatis降级为mybatis-plus的教训,就感觉这个降级是很简略。然而在革新后,运行程序却报了形如下异样 Invalid bound statement (not found): com.lybgeek.admin.file.mapper.FileMapper.insert排查从异样的字面意思是说,FIleMapper中的insert办法没有绑定。查看FileMapper.xml配置,的确没有发现绑定insert这个sql语句块。那是否加上insert的sql语句块,就能解决问题?加上的确是能解决问题。 但如果用过mybatis-plus的敌人,应该会晓得,mybatis-plus中BaseMapper曾经帮咱们封装好了一系列的单表增删改查,咱们无需写配置,就能够实现单表增删改查。所以在xml配置insert是治标不治本。 那要如何排查呢? 1、方向一:是否是包抵触引起?利用maven helper插件包抵触 从图能够看出不是包抵触引起的。 注: 因为之前吃过包抵触的亏,因而在把若依的orm改成mybatis-plus之前,就曾经去除跟mybatis相干的 jar抵触了 方向二:是不是引入不同类包的BaseMapper咱们引入的必须是 import com.baomidou.mybatisplus.core.mapper.BaseMapper;而不是 import com.baomidou.mybatisplus.mapper.BaseMapper;不过呈现这个问题,通常也是引入不同版本的mybatis-plus jar才会呈现。如果你是只用3+以上版本,他引入就只有 import com.baomidou.mybatisplus.core.mapper.BaseMapper;方向三:通用办法(断点调试)其实代码排查最怕就是异样栈被吃了,如果有异样信息,排查方向绝对比拟好找。比方这个异样,其异样栈信息为 Caused by: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.lybgeek.admin.file.mapper.FileMapper.insert at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:235) at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:53) at org.apache.ibatis.binding.MapperProxy.lambda$cachedInvoker$0(MapperProxy.java:107) at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) at org.apache.ibatis.binding.MapperProxy.cachedInvoker(MapperProxy.java:94) at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:85) at com.sun.proxy.$Proxy129.insert(Unknown Source) at com.baomidou.mybatisplus.extension.service.IService.save(IService.java:59) at com.baomidou.mybatisplus.extension.service.IService$$FastClassBySpringCGLIB$$f8525d18.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)咱们从异样栈信息,咱们能够晓得这个异样从 org.apache.ibatis.binding.MapperMethod这个类抛出,于是咱们能够把断点先设置到这边。通过源码咱们能够得悉org.apache.ibatis.mapping.MappedStatement空了,导致报了如上异样,而MappedStatement又是由 org.apache.ibatis.session.Configuration提供。而Configuration是通过 org.apache.ibatis.session.SqlSessionFactory进行设置。而后持续排查,就会发现 com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration这个主动拆卸类。外面有这么一段代码 @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { // TODO 应用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } applyConfiguration(factory); if (this.properties.getConfigurationProperties() != null) { factory.setConfigurationProperties(this.properties.getConfigurationProperties()); } if (!ObjectUtils.isEmpty(this.interceptors)) { factory.setPlugins(this.interceptors); } if (this.databaseIdProvider != null) { factory.setDatabaseIdProvider(this.databaseIdProvider); } if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); } if (this.properties.getTypeAliasesSuperType() != null) { factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType()); } if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); } if (!ObjectUtils.isEmpty(this.typeHandlers)) { factory.setTypeHandlers(this.typeHandlers); } Resource[] mapperLocations = this.properties.resolveMapperLocations(); if (!ObjectUtils.isEmpty(mapperLocations)) { factory.setMapperLocations(mapperLocations); } // TODO 对源码做了肯定的批改(因为源码适配了老旧的mybatis版本,但咱们不须要适配) Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver(); if (!ObjectUtils.isEmpty(this.languageDrivers)) { factory.setScriptingLanguageDrivers(this.languageDrivers); } Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver); // TODO 自定义枚举包 if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) { factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage()); } // TODO 此处必为非 NULL GlobalConfig globalConfig = this.properties.getGlobalConfig(); // TODO 注入填充器 this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler); // TODO 注入主键生成器 this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i)); // TODO 注入sql注入器 this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector); // TODO 注入ID生成器 this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator); // TODO 设置 GlobalConfig 到 MybatisSqlSessionFactoryBean factory.setGlobalConfig(globalConfig); return factory.getObject(); }作者在正文上都写了,要用 ...

September 10, 2020 · 2 min · jiezi

关于mybatis:mybatis-Expected-one-result-but-found

问题报错:org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3要害mapper代码: <resultMap id="rebateContractObj" type="study.mybatis.cm.entity.RebateContract" extends="study.mybatis.cm.mapper.ContractDao.contractObj"> <!-- <id property="contractId" column="contract_id" jdbcType="INTEGER"/>--> <!-- <id property="contractName" column="contractName" jdbcType="INTEGER"/>--> <result property="contractId" column="contract_id" jdbcType="INTEGER"/> <result property="processMode" column="process_mode" jdbcType="INTEGER"/> <collection property="agents" javaType="ArrayList" ofType="study.mybatis.cm.entity.RebateContractAgent"> <result property="id" column="re_id" jdbcType="INTEGER"/> <result property="postCode" column="post_code" jdbcType="VARCHAR"/> <result property="address" column="communicate_address" jdbcType="VARCHAR"/> </collection> </resultMap>继承:<resultMap id="contractObj" type="study.mybatis.cm.entity.Contract"> <id property="contractId" column="contract_id" jdbcType="INTEGER"/> <result property="zipCode" column="post_code" jdbcType="VARCHAR"/> <result property="address" column="communicate_address" jdbcType="VARCHAR"/> </resultMap>剖析:1.在rebateContractObj映射文件中,从新定义了contractId为result属性,笼罩了父类的contract_id(id属性 <id property="contractId")2.rebateContractObj中有collection汇合,外部有属性communicate_address、post_code与父mapper属性定义雷同原理:1.当定义了 <id property>时,返回后果集已<id>配置作为记录key2.当没有定义<id>时,返回后果集以全副result属性作为记录key数据分析:1.communicate_address数据不统一,且没有定义<id>属性(父mapper的被笼罩),导致以全副result作为key,所以会返回多条数据解决方案:1.去除rebateContractObj中定义的contractId属性,保留父类<id property="contractId" >2.更改继承的contractObj,继承的父mapper字段太多且和collection雷同了,更改或去除反复字段名

September 9, 2020 · 1 min · jiezi

关于mybatis:MyBatis框架

MyBatis简介MyBatis是一个优良的长久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只须要关注SQL自身,而不须要破费精力去解决例如注册驱动、创立connection、创立statement、手动设置参数、后果集检索等jdbc繁冗的过程代码。Mybatis通过xml或注解的形式将要执行的各statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最初由mybatis框架执行sql并将后果映射成java对象并返回。总之,Mybatis对JDBC拜访数据库的过程进行了封装,简化了JDBC代码,解决JDBC将后果集封装为Java对象的麻烦。 MyBatis的劣势1.应用传统形式拜访数据库(1)应用JDBC拜访数据库有大量反复代码(2)JDBC本身没有连接池,会繁琐的创立连贯和敞开连贯,效率低(3)SQL是写死在程序中,一旦批改SQL,须要对类进行从新编译(4)对查问SQL执行后返回的ResultSet对象,须要手动解决,有时会特地麻烦 2.应用MyBatis框架拜访数据库(1)MyBatis对JDBC进行了封装,能够简化JDBC代码(2)MyBatis本身反对连接池(也能够配置其余的连接池),因而能够进步程序的效率(3)MyBatis是将SQL配置在mapper文件中,批改SQL只是批改配置文件,类不要编译(4)对查问SQL执行后返回的ResultSet对象,MyBatis会帮咱们解决,转换成Java对象总之,JDBC中的所有问题在MyBatis框架中简直失去了解决! MyBatis架构图剖析Mybatis架构图:(1)mybatis-config.xml是MyBatis的外围配置文件,通过其中的配置能够生成SqlSessionFactory,也就是SqlSession工厂(2)基于SqlSessionFactory能够生成SqlSession对象(3)SqlSeesion是个既能够发送SQL去执行,并返回后果,相似于JDBC中的Connerction对象,也就是MyBatis中至关重要的一个对象(4)Executor是SqlSession底层的对象,用于执行SQL语句(5)MapperStatement对象也是SqlSession底层的对象那个,用于接管输出映射(SQL语句中的参数),以及做输入映射(即SQL查问的后果映射成相应的后果) MyBatis配置文件剖析mybatis-config.xml配置文件:`<?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 > <!-- 1.配置环境,可配置多个环境(比方:develop开发、test测试) --><environments default="develop"> <environment id="develop"> <!-- 1.1.配置事务管理形式:JDBC/MANAGED JDBC:将事务交给JDBC治理(举荐) MANAGED:本人治理事务 --> <transactionManager type="JDBC"></transactionManager> <!-- 1.2.配置数据源,即连接池 JNDI/POOLED/UNPOOLED JNDI:已过期 POOLED:应用连接池(举荐) UNPOOLED:不应用连接池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment></environments><!-- 2.导入Mapper配置文件,如果mapper文件有多个,能够通过多个mapper标签导入 --><mappers> <mapper resource="EmpMapper.xml"/></mappers></configuration>` MyBatis中的占位符在增删改查操作中,SQL语句中的值是写死在SQL语句中,而理论开发中,次处的值往往是用户提交过去的值,因而这里咱们须要将SQL中写死的值替换为占位符。在mybatis中占位符有两个,别离是#{}占位符和${}占位符: **#{}:相当于JDBC的问号(?)占位符,是为SQL语句中的参数进行占位,大部分状况都是应用#{}占位符;并且#{}占位符是为字符串或者日期类型的值进行占位时,在参数值传过来替换占位符的同时,会进行本义解决(在字符串或日期类型的值的两边加上单引号);${}:**是为SQL片段(字符串)进行展位,将传过来的SQL片段间接拼接**在${}占位符所在的地位,不会进行任何的转移操作。(因为是间接将参数拼接在SQL语句中,因而可能会引发SQL注入攻打问题.须要留神的是:应用${}占位符为SQL语句中的片段占位时,即便只有一个占位符,须要传的也只有一个参数,也须要将参数进行封装在传递!** Mapper接口开发(一)为什么要应用Mapper接口开发?1.在测试用例中,在调用Session的办法时候,都会传入要调用的SQL的namespace+id名称,这不是必须的,能够只传入id即可。然而如果在Mybatis的环境中有多个雷同id的映射名称,就会报错,所以,个别状况下,调用办法最好还是应用namespace+id。2.然而,namespace+id的应用形式很容易报错,因为String类型的,没有查看。所以mybatis提供了一种十分好的设计形式来防止这种问题,即Mapper接口。**接口的开发标准:namespace的值=接口的包名+类名留神:***1、包名+类名=xxxMapper.xml中的namespace的值2、接口中办法名=mapper.xml中具体的SQL语句定义的值3、办法的返回值和参数要和映射文件中统一(当数据库的字段名和对象的属性名统一时,能够简略属性resultType。然而数据库中的字段名称和对象中的属性名称不统一时,就须要resultMap属性)* public List<User> all(); //这是接口中的办法(二)MyBatis怎么做的?@Test public void testMapper(){ SqlSession session = MyBatisUtil.openSession(); try{ UserMapper mapper = session.getMapper(UserMapper.class); System.out.println(mapper.getClass().getName()); }finally{ session.close(); } }打印后果:$Proxy4很简略,mybatis为接口做了一个动静代理。在执行UserMapper接口下面的办法时,参考接口的群路径名,即可找到对应的UserMapper.xml,在执行接口下面的每一个参数的时候,实际上就是执行namespace+id,mybatis在依据定义的办法的元素,抉择调用适合的session的办法来执行,并传入参数就能够。应用Mapper接口的形式,在Spring+MyBatis也十分不便。因为咱们能够间接把Mapper接口看成dao接口了。 (三)接口开发的三个特点1、Mapper接口的办法名和mapper.xml中定义sql的id值雷同2、Mapper接口的办法接口的参数类型和mapper.xml定义的sql的parameterType类型雷同3、Mapper接口办法的返回值类型和mapper.xml中定义的sql的resultType的类型雷同 ...

September 3, 2020 · 1 min · jiezi

关于mybatis:关于mybatis

mybatis框架的简介和劣势MyBatis 本是apache的一个开源我的项目iBatis,2010年这个我的项目由apache software foundation 迁徙到了google code,并且改名为MyBatis 。2013年11月迁徙到Github。 Mybatis是一个优良的长久层框架,他对jdbc操作数据库的过程进行了封装,使咱们只须要关注Sql语句自身,不须要破费精力去解决例如注册驱动、创立connection(连贯)、创立statement(申明)、手动设置参数、后果集检索等繁琐的过程代码。mybatis通过xml或注解的形式将要执行的各种statement配置起来,并通过java对象和statement中的sql进行映射生成最终sql语句,最初由mybatis框架执行sql并将后果映射成java对象并返回。MyBatis架构图(1)mybatis-config.xml是MyBatis框架的外围配置文件,通过其中的配置能够生成SqlSessionFactory,也就是工厂。(2)基于SqlSessionFactory能够生成SqlSession对象(3)SqlSession是一个既能够发送SQL去执行,并返回后果的对象(4)Executor是SqlSession底层的对象,用于执行SQL语句(5)MapperStatement对象也是SqlSession底层的对象,用于接管输出映射(SQL语句中的参数),以及做输入映射(行将SQL查问的后果映射成相应的后果) 配置及应用MyBatis框架1.创立maven工程,并在pom.xml中导入所需jar包(mybatis、mysql启动等)[(https://mybatis.org/mybatis-3)MyBatis官网地址,留神版本]2.配置MyBatis全局配置文件mybatis-config.xml(在resource源码文件夹下) (1)头信息(验证xml文件的正确性)(2)配置数据库环境,是否应用连接池,Driver类全类名,须要连贯的库,账户明码等 能够把数据库的连贯信息独自拿进去,不便前期保护(更改明码等) 在源码文件夹中创立.properties的File文件,写入Driver,url,username,password等信息,在主配置文件mybatis-config.xml文件中导入文件,将主配置文件的Driver信息写成${}模式的占位符,名称和.properties文件的信息对应(3)配置映射文件(mapper)3.配置映射文件mapper (1)增加头信息,束缚标准(2)在<mapper>标签下增加属性namespace(命名空间)(3)在<mapper>标签外部书写Sql语句,例如<select id="id惟一" resultype="后果类型">select * from 表名 (能够应用占位符${})</select>4.在java文件夹中创立Emp类,用于封装查问后返回的对象,属性与数据库中各列的名字对应5.尝试连贯一.(创立测试类后筹备工作,能够用@Before注解,在所有办法之前加载) (1)读取mybatis-config.xml文件InputStream in = Resource.getResourceAsStream("mybatis-config.xml")(2)基于读取信息创立一个SqlSessionFactory对象SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build( in );(3)基于SqlSessionFactory对象获取一个SqlSession对象session = factory.openSession(true);//true设置主动提交事务二.执行语句 应用SqlSession对象的update等办法,传入映射文件的namespace.语句id执行操作,如果语句中有占位符能够传入(namespace.id,map), map用与封装多个占位符 返回后果影响行数或者查问后果,应用foreach遍历

August 30, 2020 · 1 min · jiezi

关于mybatis:MyBatis整合Redis实现二级缓存

MyBatis框架提供了二级缓存接口,咱们只须要实现它再开启配置就能够应用了。特地留神,咱们要解决缓存穿透、缓存穿透和缓存雪崩的问题,同时也要保障缓存性能。具体实现阐明,间接看代码正文吧! 1、开启配置SpringBoot配置mybatis: configuration: cache-enabled: true2、Redis配置以及服务接口RedisConfig.javapackage com.leven.mybatis.api.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;/** * Redis缓存配置 * @author Leven * @date 2019-09-07 */@Configurationpublic class RedisConfig { /** * 配置自定义redisTemplate * @return redisTemplate */ @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); // 应用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(mapper); template.setKeySerializer(stringRedisSerializer); template.setValueSerializer(jackson2JsonRedisSerializer); template.setHashKeySerializer(stringRedisSerializer); template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; }}RedisService.javapackage com.leven.mybatis.core.service;import java.util.*;import java.util.concurrent.TimeUnit;/** * redis根底服务接口 * @author Leven * @date 2019-09-07 */public interface RedisService {// =============================common============================ /** * 指定缓存生效工夫 * @param key 键 * @param time 工夫(秒) */ void expire(String key, long time); /** * 指定缓存生效工夫 * @param key 键 * @param expireAt 生效工夫点 * @return 处理结果 */ void expireAt(String key, Date expireAt); /** * 依据key 获取过期工夫 * @param key 键 不能为null * @return 工夫(秒) 返回0代表为永恒无效 */ Long getExpire(String key); /** * 判断key是否存在 * @param key 键 * @return true 存在 false不存在 */ Boolean hasKey(String key); /** * 删除缓存 * @param key 能够传一个值 或多个 */ void delete(String... key); /** * 删除缓存 * @param keys 能够传一个值 或多个 */ void delete(Collection<String> keys); // ============================String============================= /** * 一般缓存获取 * @param key 键 * @return 值 */ Object get(String key); /** * 一般缓存放入 * @param key 键 * @param value 值 */ void set(String key, Object value); /** * 一般缓存放入并设置工夫 * @param key 键 * @param value 值 * @param time 工夫(秒) time要大于0 如果time小于等于0 将设置无限期 */ void set(String key, Object value, long time); /** * 一般缓存放入并设置工夫 * @param key 键 * @param value 值 * @param time 工夫(秒) time要大于0 如果time小于等于0 将设置无限期 */ void set(String key, Object value, long time, TimeUnit timeUnit); /** * 递增 * @param key 键 * @param value 要减少几(大于0) * @return 递增后后果 */ Long incr(String key, long value); /** * 递加 * @param key 键 * @param value 要缩小几(大于0) * @return 递加后后果 */ Long decr(String key, long value); // ================================Map================================= /** * HashGet * @param key 键 不能为null * @param item 项 不能为null * @return 值 */ Object hashGet(String key, String item); /** * 获取hashKey对应的所有键值 * @param key 键 * @return 对应的多个键值 */ Map<Object, Object> hashEntries(String key); /** * HashSet * @param key 键 * @param map 对应多个键值 */ void hashSet(String key, Map<String, Object> map); /** * HashSet 并设置工夫 * @param key 键 * @param map 对应多个键值 * @param time 工夫(秒) */ void hashSet(String key, Map<String, Object> map, long time); /** * 向一张hash表中放入数据,如果不存在将创立 * @param key 键 * @param item 项 * @param value 值 */ void hashSet(String key, String item, Object value); /** * 向一张hash表中放入数据,如果不存在将创立 * @param key 键 * @param item 项 * @param value 值 * @param time 工夫(秒) 留神:如果已存在的hash表有工夫,这里将会替换原有的工夫 */ void hashSet(String key, String item, Object value, long time); /** * 删除hash表中的值 * @param key 键 不能为null * @param item 项 能够使多个 不能为null */ void hashDelete(String key, Object... item); /** * 删除hash表中的值 * @param key 键 不能为null * @param items 项 能够使多个 不能为null */ void hashDelete(String key, Collection items); /** * 判断hash表中是否有该项的值 * @param key 键 不能为null * @param item 项 不能为null * @return true 存在 false不存在 */ Boolean hashHasKey(String key, String item); /** * hash递增 如果不存在,就会创立一个 并把新增后的值返回 * @param key 键 * @param item 项 * @param value 要减少几(大于0) * @return 递增后后果 */ Double hashIncr(String key, String item, double value); /** * hash递加 * @param key 键 * @param item 项 * @param value 要缩小记(小于0) * @return 递加后后果 */ Double hashDecr(String key, String item, double value); // ============================set============================= /** * 依据key获取Set中的所有值 * @param key 键 * @return set汇合 */ Set<Object> setGet(String key); /** * 依据value从一个set中查问,是否存在 * @param key 键 * @param value 值 * @return true 存在 false不存在 */ Boolean setIsMember(String key, Object value); /** * 将数据放入set缓存 * @param key 键 * @param values 值 能够是多个 * @return 胜利个数 */ Long setAdd(String key, Object... values); /** * 将数据放入set缓存 * @param key 键 * @param values 值 能够是多个 * @return 胜利个数 */ Long setAdd(String key, Collection values); /** * 将set数据放入缓存 * @param key 键 * @param time 工夫(秒) * @param values 值 能够是多个 * @return 胜利个数 */ Long setAdd(String key, long time, Object... values); /** * 获取set缓存的长度 * @param key 键 * @return set长度 */ Long setSize(String key); /** * 移除值为value的 * @param key 键 * @param values 值 能够是多个 * @return 移除的个数 */ Long setRemove(String key, Object... values); // ===============================list================================= /** * 获取list缓存的内容 * @param key 键 * @param start 开始 * @param end 完结 0 到 -1代表所有值 * @return 缓存列表 */ List<Object> listRange(String key, long start, long end); /** * 获取list缓存的长度 * @param key 键 * @return 长度 */ Long listSize(String key); /** * 通过索引 获取list中的值 * @param key 键 * @param index 索引 index>=0时, 0 表头,1 第二个元素,顺次类推;index<0时,-1,表尾,-2倒数第二个元素,顺次类推 * @return 值 */ Object listIndex(String key, long index); /** * 将list放入缓存 * @param key 键 * @param value 值 */ void listRightPush(String key, Object value); /** * 将list放入缓存 * @param key 键 * @param value 值 * @param time 工夫(秒) */ void listRightPush(String key, Object value, long time); /** * 将list放入缓存 * @param key 键 * @param value 值 */ void listRightPushAll(String key, List<Object> value); /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 工夫(秒) */ void listRightPushAll(String key, List<Object> value, long time); /** * 依据索引批改list中的某条数据 * @param key 键 * @param index 索引 * @param value 值 */ void listSet(String key, long index, Object value); /** * 移除N个值为value * @param key 键 * @param count 移除多少个 * @param value 值 * @return 移除的个数 */ Long listRemove(String key, long count, Object value);}RedisServiceImpl.javapackage com.leven.mybatis.core.service.impl;import com.leven.commons.model.exception.SPIException;import com.leven.mybatis.model.constant.Constant;import com.leven.mybatis.core.service.RedisService;import com.leven.mybatis.model.constant.ExceptionCode;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import java.util.*;import java.util.concurrent.TimeUnit;/** * redis根底服务接口实现 * @author Leven * @date 2019-09-07 */@Slf4j@Servicepublic class RedisServiceImpl implements RedisService { /** * */ private static final String PREFIX = Constant.APPLICATION; @Autowired private RedisTemplate<String, Object> redisTemplate; // =============================common============================ /** * 指定缓存生效工夫 * @param key 键 * @param time 工夫(秒) */ @Override public void expire(String key, long time) { redisTemplate.expire(getKey(key), time, TimeUnit.SECONDS); } /** * 指定缓存生效工夫 * @param key 键 * @param expireAt 生效工夫点 * @return 处理结果 */ @Override public void expireAt(String key, Date expireAt) { redisTemplate.expireAt(getKey(key), expireAt); } /** * 依据key 获取过期工夫 * @param key 键 不能为null * @return 工夫(秒) 返回0代表为永恒无效 */ @Override public Long getExpire(String key) { return redisTemplate.getExpire(getKey(key), TimeUnit.SECONDS); } /** * 判断key是否存在 * @param key 键 * @return true 存在 false不存在 */ @Override public Boolean hasKey(String key) { return redisTemplate.hasKey(getKey(key)); } /** * 删除缓存 * @param keys 能够传一个值 或多个 */ @Override public void delete(String... keys) { if (keys != null && keys.length > 0) { if (keys.length == 1) { redisTemplate.delete(getKey(keys[0])); } else { List<String> keyList = new ArrayList<>(keys.length); for (String key : keys) { keyList.add(getKey(key)); } redisTemplate.delete(keyList); } } } /** * 删除缓存 * @param keys 能够传一个值 或多个 */ @Override public void delete(Collection<String> keys) { if (keys != null && !keys.isEmpty()) { List<String> keyList = new ArrayList<>(keys.size()); for (String key : keys) { keyList.add(getKey(key)); } redisTemplate.delete(keyList); } } // ============================String============================= /** * 一般缓存获取 * @param key 键 * @return 值 */ @Override public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(getKey(key)); } /** * 一般缓存放入 * @param key 键 * @param value 值 */ @Override public void set(String key, Object value) { redisTemplate.opsForValue().set(getKey(key), value); } /** * 一般缓存放入并设置工夫 * @param key 键 * @param value 值 * @param time 工夫(秒) time要大于0 如果time小于等于0 将设置无限期 */ @Override public void set(String key, Object value, long time) { set(key, value, time, TimeUnit.SECONDS); } /** * 一般缓存放入并设置工夫 * @param key 键 * @param value 值 * @param time 工夫 time要大于0 如果time小于等于0 将设置无限期 * @param timeUnit 工夫单位 */ @Override public void set(String key, Object value, long time, TimeUnit timeUnit) { if (time > 0) { redisTemplate.opsForValue().set(getKey(key), value, time, timeUnit); } else { set(getKey(key), value); } } /** * 递增 * @param key 键 * @param value 要减少几(大于0) * @return 递增后后果 */ @Override public Long incr(String key, long value) { if (value < 1) { throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"递增因子必须大于0"); } return redisTemplate.opsForValue().increment(getKey(key), value); } /** * 递加 * @param key 键 * @param value 要缩小几(大于0) * @return 递加后后果 */ @Override public Long decr(String key, long value) { if (value < 1) { throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"递加因子必须大于0"); } return redisTemplate.opsForValue().decrement(getKey(key), value); } // ================================Map================================= /** * HashGet * @param key 键 不能为null * @param item 项 不能为null * @return 值 */ @Override public Object hashGet(String key, String item) { return redisTemplate.opsForHash().get(getKey(key), item); } /** * 获取hashKey对应的所有键值 * @param key 键 * @return 对应的多个键值 */ @Override public Map<Object, Object> hashEntries(String key) { return redisTemplate.opsForHash().entries(getKey(key)); } /** * HashSet * @param key 键 * @param map 对应多个键值 */ @Override public void hashSet(String key, Map<String, Object> map) { redisTemplate.opsForHash().putAll(getKey(key), map); } /** * HashSet 并设置工夫 * @param key 键 * @param map 对应多个键值 * @param time 工夫(秒) */ @Override public void hashSet(String key, Map<String, Object> map, long time) { String k = getKey(key); redisTemplate.opsForHash().putAll(k, map); if (time > 0) { expire(k, time); } } /** * 向一张hash表中放入数据,如果不存在将创立 * @param key 键 * @param item 项 * @param value 值 */ @Override public void hashSet(String key, String item, Object value) { redisTemplate.opsForHash().putIfAbsent(getKey(key), item, value); } /** * 向一张hash表中放入数据,如果不存在将创立 * @param key 键 * @param item 项 * @param value 值 * @param time 工夫(秒) 留神:如果已存在的hash表有工夫,这里将会替换原有的工夫 */ @Override public void hashSet(String key, String item, Object value, long time) { String k = getKey(key); redisTemplate.opsForHash().putIfAbsent(k, item, value); if (time > 0) { expire(k, time); } } /** * 删除hash表中的值 * @param key 键 不能为null * @param item 项 能够使多个 不能为null */ @Override public void hashDelete(String key, Object... item) { redisTemplate.opsForHash().delete(getKey(key), item); } /** * 删除hash表中的值 * @param key 键 不能为null * @param items 项 能够使多个 不能为null */ @Override public void hashDelete(String key, Collection items) { redisTemplate.opsForHash().delete(getKey(key), items.toArray()); } /** * 判断hash表中是否有该项的值 * @param key 键 不能为null * @param item 项 不能为null * @return true 存在 false不存在 */ @Override public Boolean hashHasKey(String key, String item) { return redisTemplate.opsForHash().hasKey(getKey(key), item); } /** * hash递增 如果不存在,就会创立一个 并把新增后的值返回 * @param key 键 * @param item 项 * @param value 要减少几(大于0) * @return 递增后后果 */ @Override public Double hashIncr(String key, String item, double value) { if (value < 1) { throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"递增因子必须大于0"); } return redisTemplate.opsForHash().increment(getKey(key), item, value); } /** * hash递加 * @param key 键 * @param item 项 * @param value 要缩小记(小于0) * @return 递加后后果 */ @Override public Double hashDecr(String key, String item, double value) { if (value < 1) { throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"递加因子必须大于0"); } return redisTemplate.opsForHash().increment(getKey(key), item, -value); } // ============================set============================= /** * 依据key获取Set中的所有值 * @param key 键 * @return set汇合 */ @Override public Set<Object> setGet(String key) { return redisTemplate.opsForSet().members(getKey(key)); } /** * 依据value从一个set中查问,是否存在 * @param key 键 * @param value 值 * @return true 存在 false不存在 */ @Override public Boolean setIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(getKey(key), value); } /** * 将数据放入set缓存 * @param key 键 * @param values 值 能够是多个 * @return 胜利个数 */ @Override public Long setAdd(String key, Object... values) { return redisTemplate.opsForSet().add(getKey(key), values); } /** * 将数据放入set缓存 * @param key 键 * @param values 值 能够是多个 * @return 胜利个数 */ @Override public Long setAdd(String key, Collection values) { return redisTemplate.opsForSet().add(getKey(key), values.toArray()); } /** * 将set数据放入缓存 * @param key 键 * @param time 工夫(秒) * @param values 值 能够是多个 * @return 胜利个数 */ @Override public Long setAdd(String key, long time, Object... values) { String k = getKey(key); Long count = redisTemplate.opsForSet().add(k, values); if (time > 0){ expire(k, time); } return count; } /** * 获取set缓存的长度 * @param key 键 * @return set长度 */ @Override public Long setSize(String key) { return redisTemplate.opsForSet().size(getKey(key)); } /** * 移除值为value的 * @param key 键 * @param values 值 能够是多个 * @return 移除的个数 */ @Override public Long setRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(getKey(key), values); } // ===============================list================================= /** * 获取list缓存的内容 * @param key 键 * @param start 开始 * @param end 完结 0 到 -1代表所有值 * @return 缓存列表 */ @Override public List<Object> listRange(String key, long start, long end) { return redisTemplate.opsForList().range(getKey(key), start, end); } /** * 获取list缓存的长度 * @param key 键 * @return 长度 */ @Override public Long listSize(String key) { return redisTemplate.opsForList().size(getKey(key)); } /** * 通过索引 获取list中的值 * @param key 键 * @param index 索引 index>=0时, 0 表头,1 第二个元素,顺次类推;index<0时,-1,表尾,-2倒数第二个元素,顺次类推 * @return 值 */ @Override public Object listIndex(String key, long index) { return redisTemplate.opsForList().index(getKey(key), index); } /** * 将list放入缓存 * @param key 键 * @param value 值 */ @Override public void listRightPush(String key, Object value) { redisTemplate.opsForList().rightPush(getKey(key), value); } /** * 将list放入缓存 * @param key 键 * @param value 值 * @param time 工夫(秒) */ @Override public void listRightPush(String key, Object value, long time) { String k = getKey(key); redisTemplate.opsForList().rightPush(k, value); if (time > 0){ expire(k, time); } } /** * 将list放入缓存 * @param key 键 * @param value 值 */ @Override public void listRightPushAll(String key, List<Object> value) { redisTemplate.opsForList().rightPushAll(getKey(key), value); } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 工夫(秒) */ @Override public void listRightPushAll(String key, List<Object> value, long time) { String k = getKey(key); redisTemplate.opsForList().rightPushAll(k, value); if (time > 0) { expire(k, time); } } /** * 依据索引批改list中的某条数据 * @param key 键 * @param index 索引 * @param value 值 */ @Override public void listSet(String key, long index, Object value) { redisTemplate.opsForList().set(getKey(key), index, value); } /** * 移除N个值为value * @param key 键 * @param count 移除多少个 * @param value 值 * @return 移除的个数 */ @Override public Long listRemove(String key, long count, Object value) { return redisTemplate.opsForList().remove(getKey(key), count, value); } private String getKey(String key) { return PREFIX + ":" + key; }}3、实现MyBatis的Cache接口MybatisRedisCache.javapackage com.leven.mybatis.core.cache;import com.leven.commons.core.util.ApplicationContextUtils;import com.leven.commons.model.exception.SPIException;import com.leven.mybatis.core.service.RedisService;import com.leven.mybatis.model.constant.ExceptionCode;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.RandomUtils;import org.apache.ibatis.cache.Cache;import java.security.MessageDigest;import java.util.HashSet;import java.util.Map;import java.util.Set;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/** * MyBatis二级缓存Redis实现 * 重点解决以下几个问题 * 1、缓存穿透:存储空值解决,MyBatis框架实现 * 2、缓存击穿:应用互斥锁,咱们本人实现 * 3、缓存雪崩:缓存有效期设置为一个随机范畴,咱们本人实现 * 4、读写性能:redis key不能过长,会影响性能,这里应用SHA-256计算摘要当成key * @author Leven * @date 2019-09-07 */@Slf4jpublic class MybatisRedisCache implements Cache { /** * 对立字符集 */ private static final String CHARSET = "utf-8"; /** * key摘要算法 */ private static final String ALGORITHM = "SHA-256"; /** * 对立缓存头 */ private static final String CACHE_NAME = "MyBatis:"; /** * 读写锁:解决缓存击穿 */ private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /** * 表空间ID:不便前面的缓存清理 */ private final String id; /** * redis服务接口:提供根本的读写和清理 */ private static volatile RedisService redisService; /** * 信息摘要 */ private volatile MessageDigest messageDigest; /////////////////////// 解决缓存雪崩,具体范畴依据业务须要设置正当值 ////////////////////////// /** * 缓存最小有效期 */ private static final int MIN_EXPIRE_MINUTES = 60; /** * 缓存最大有效期 */ private static final int MAX_EXPIRE_MINUTES = 120; /** * MyBatis给每个表空间初始化的时候要用到 * @param id 其实就是namespace的值 */ public MybatisRedisCache(String id) { if (id == null) { throw new IllegalArgumentException("Cache instances require an ID"); } this.id = id; } /** * 获取ID * @return 实在值 */ @Override public String getId() { return id; } /** * 创立缓存 * @param key 其实就是sql语句 * @param value sql语句查问后果 */ @Override public void putObject(Object key, Object value) { try { String strKey = getKey(key); // 有效期为1~2小时之间随机,避免雪崩 int expireMinutes = RandomUtils.nextInt(MIN_EXPIRE_MINUTES, MAX_EXPIRE_MINUTES); getRedisService().set(strKey, value, expireMinutes, TimeUnit.MINUTES); log.debug("Put cache to redis, id={}", id); } catch (Exception e) { log.error("Redis put failed, id=" + id, e); } } /** * 读取缓存 * @param key 其实就是sql语句 * @return 缓存后果 */ @Override public Object getObject(Object key) { try { String strKey = getKey(key); log.debug("Get cache from redis, id={}", id); return getRedisService().get(strKey); } catch (Exception e) { log.error("Redis get failed, fail over to db", e); return null; } } /** * 删除缓存 * @param key 其实就是sql语句 * @return 后果 */ @Override public Object removeObject(Object key) { try { String strKey = getKey(key); getRedisService().delete(strKey); log.debug("Remove cache from redis, id={}", id); } catch (Exception e) { log.error("Redis remove failed", e); } return null; } /** * 缓存清理 * 网上好多博客这里用了flushDb甚至是flushAll,感觉好坑鸭! * 应该是依据表空间进行清理 */ @Override public void clear() { try { log.debug("clear cache, id={}", id); String hsKey = CACHE_NAME + id; // 获取CacheNamespace所有缓存key Map<Object, Object> idMap = getRedisService().hashEntries(hsKey); if (!idMap.isEmpty()) { Set<Object> keySet = idMap.keySet(); Set<String> keys = new HashSet<>(keySet.size()); keySet.forEach(item -> keys.add(item.toString())); // 清空CacheNamespace所有缓存 getRedisService().delete(keys); // 清空CacheNamespace getRedisService().delete(hsKey); } } catch (Exception e) { log.error("clear cache failed", e); } } /** * 获取缓存大小,临时没用上 * @return 长度 */ @Override public int getSize() { return 0; } /** * 获取读写锁:为了解决缓存击穿 * @return 锁 */ @Override public ReadWriteLock getReadWriteLock() { return readWriteLock; } /** * 计算出key的摘要 * @param cacheKey CacheKey * @return 字符串key */ private String getKey(Object cacheKey) { String cacheKeyStr = cacheKey.toString(); log.debug("count hash key, cache key origin string:{}", cacheKeyStr); String strKey = byte2hex(getSHADigest(cacheKeyStr)); log.debug("hash key:{}", strKey); String key = CACHE_NAME + strKey; // 在redis额定保护CacheNamespace创立的key,clear的时候只清理以后CacheNamespace的数据 getRedisService().hashSet(CACHE_NAME + id, key, "1"); return key; } /** * 获取信息摘要 * @param data 待计算字符串 * @return 字节数组 */ private byte[] getSHADigest(String data) { try { if (messageDigest == null) { synchronized (MessageDigest.class) { if (messageDigest == null) { messageDigest = MessageDigest.getInstance(ALGORITHM); } } } return messageDigest.digest(data.getBytes(CHARSET)); } catch (Exception e) { log.error("SHA-256 digest error: ", e); throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"SHA-256 digest error, id=" + id + "."); } } /** * 字节数组转16进制字符串 * @param bytes 待转换数组 * @return 16进制字符串 */ private String byte2hex(byte[] bytes) { StringBuilder sign = new StringBuilder(); for (byte aByte : bytes) { String hex = Integer.toHexString(aByte & 0xFF); if (hex.length() == 1) { sign.append("0"); } sign.append(hex.toUpperCase()); } return sign.toString(); } /** * 获取Redis服务接口 * 应用双重查看保障线程平安 * @return 服务实例 */ private RedisService getRedisService() { if (redisService == null) { synchronized (RedisService.class) { if (redisService == null) { redisService = ApplicationContextUtils.getBeanByClass(RedisService.class); } } } return redisService; }}

August 18, 2020 · 14 min · jiezi

关于mybatis:resultMap-使用

resultmap标签是mybatis框架中罕用的一个元素,也是十分重要的映射元素,用于实现mybatis的高级映射. 其利用场景: 1)表中字段与pojo类中属性名不统一时,(如stu_id/stuId). <resultMap type="返回类型全限定名" id="命名"> <id property="id" column="id" /> <result property="name" column="name"/> <result property="note" column="note"/> </resultMap>应用办法如上,type为返回类型全限定名,id为本人为resultmap定义的名字,在sql标签上resultmap出写id,resultmap中<id>必须是表中定义的主键,<result>为表中其余属性,其中的property属性为pojo定义的属性名,column为表中的字段名. 2)sql语句嵌套查问:当咱们查问的是1对n关系时,能够抉择collection元素,同样也是property属性为pojo定义的属性名,column为表中的字段名,select属性指向全限定名的另一个Dao层的办法. <resultMap type="返回类型全限定名" id="命名"> <id property="id" column="id" /> <result property="name" column="name"/> <result property="note" column="note"/> <!-- collection个别利用于one2many查问 --> <collection property="menuIds" column="id"> <select="全限定类名"/> </collection></resultMap>3)多表关联查问:通过左外或者右外连贯,将所需的表关联在一起进行查问,将基准表的信息写在<id><result>中,关联的表一个表写在一个<collection>中,ofType属性为property数组中,单个数据的类型. <select id="findObjectById" resultMap="sysRoleMenuVo"> select r.id,r.name,r.note,rm.menu_id from sys_roles r left join sys_role_menus rm on r.id=rm.role_id where r.id=#{id}</select><resultMap type="com.cy.pj.sys.pojo.SysRoleMenuVo" id="sysRoleMenuVo"> <id property="id" column="id" /> <result property="name" column="name"/> <result property="note" column="note"/> <!-- collection个别利用于one2many查问 --> <collection property="menuIds" ofType="integer"> <result column="menu_id"/> </collection></resultMap>

August 17, 2020 · 1 min · jiezi

关于mybatis:resultMap-使用

resultmap标签是mybatis框架中罕用的一个元素,也是十分重要的映射元素,用于实现mybatis的高级映射. 其利用场景: 1)表中字段与pojo类中属性名不统一时,(如stu_id/stuId). <resultMap type="返回类型全限定名" id="命名"> <id property="id" column="id" /> <result property="name" column="name"/> <result property="note" column="note"/> </resultMap>应用办法如上,type为返回类型全限定名,id为本人为resultmap定义的名字,在sql标签上resultmap出写id,resultmap中<id>必须是表中定义的主键,<result>为表中其余属性,其中的property属性为pojo定义的属性名,column为表中的字段名. 2)sql语句嵌套查问:当咱们查问的是1对n关系时,能够抉择collection元素,同样也是property属性为pojo定义的属性名,column为表中的字段名,select属性指向全限定名的另一个Dao层的办法. <resultMap type="返回类型全限定名" id="命名"> <id property="id" column="id" /> <result property="name" column="name"/> <result property="note" column="note"/> <!-- collection个别利用于one2many查问 --> <collection property="menuIds" column="id"> <select="全限定类名"/> </collection></resultMap>3)多表关联查问:通过左外或者右外连贯,将所需的表关联在一起进行查问,将基准表的信息写在<id><result>中,关联的表一个表写在一个<collection>中,ofType属性为property数组中,单个数据的类型. <select id="findObjectById" resultMap="sysRoleMenuVo"> select r.id,r.name,r.note,rm.menu_id from sys_roles r left join sys_role_menus rm on r.id=rm.role_id where r.id=#{id}</select><resultMap type="com.cy.pj.sys.pojo.SysRoleMenuVo" id="sysRoleMenuVo"> <id property="id" column="id" /> <result property="name" column="name"/> <result property="note" column="note"/> <!-- collection个别利用于one2many查问 --> <collection property="menuIds" ofType="integer"> <result column="menu_id"/> </collection></resultMap>

August 17, 2020 · 1 min · jiezi

关于mybatis:MBG

mybatis-generator-config_1_0.dtd mybatis生成代码的配置文件的文档类型定义 配置示例https://www.jianshu.com/p/e09d2370b796 生成代码流程,以生成java代码 Dao层为例

August 10, 2020 · 1 min · jiezi

关于mybatis:MBG

mybatis-generator-config_1_0.dtd mybatis生成代码的配置文件的文档类型定义 配置示例https://www.jianshu.com/p/e09d2370b796 生成代码流程,以生成java代码 Dao层为例

August 10, 2020 · 1 min · jiezi

关于mybatis:Mybatis-的一级二级缓存

一级缓存 Mybatis对缓存提供反对,然而在没有配置的默认状况下,它只开启一级缓存,一级缓存只是绝对于同一个SqlSession而言。所以在参数和SQL齐全一样的状况下,咱们应用同一个SqlSession对象调用一个Mapper办法,往往只执行一次SQL,因为应用SelSession第一次查问后,MyBatis会将其放在缓存中,当前再查问的时候,如果没有申明须要刷新,并且缓存没有超时的状况下,SqlSession都会取出以后缓存的数据,而不会再次发送SQL到数据库。 为什么要应用一级缓存,不必多说也晓得个大略。然而还有几个问题咱们要留神一下。 1、一级缓存的生命周期有多长? a、MyBatis在开启一个数据库会话时,会 创立一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话完结时,SqlSession对象及其外部的Executor对象还有PerpetualCache对象也一并开释掉。 b、如果SqlSession调用了close()办法,会开释掉一级缓存PerpetualCache对象,一级缓存将不可用。 c、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,然而该对象仍可应用。 d、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,然而该对象能够持续应用    2、怎么判断某两次查问是完全相同的查问? mybatis认为,对于两次查问,如果以下条件都齐全一样,那么就认为它们是完全相同的两次查问。 2.1 传入的statementId 2.2 查问时要求的后果集中的后果范畴 2.3. 这次查问所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() ) 2.4 传递给java.sql.Statement要设置的参数值 二级缓存: MyBatis的二级缓存是Application级别的缓存,它能够进步对数据库查问的效率,以进步利用的性能。 MyBatis的缓存机制整体设计以及二级缓存的工作模式 SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开启须要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置办法很简略,只须要在映射XML文件配置就能够开启缓存了<cache/>,如果咱们配置了二级缓存就意味着: 映射语句文件中的所有select语句将会被缓存。映射语句文件中的所欲insert、update和delete语句会刷新缓存。缓存会应用默认的Least Recently Used(LRU,最近起码应用的)算法来发出。依据时间表,比方No Flush Interval,(CNFI没有刷新距离),缓存不会以任何工夫程序来刷新。缓存会存储列表汇合或对象(无论查询方法返回什么)的1024个援用缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且能够平安的被调用者批改,不烦扰其余调用者或线程所做的潜在批改。实际:一、创立一个POJO Bean并序列化 因为二级缓存的数据不肯定都是存储到内存中,它的存储介质多种多样,所以须要给缓存的对象执行序列化。(如果存储在内存中的话,实测不序列化也能够的。) package com.yihaomen.mybatis.model;import com.yihaomen.mybatis.enums.Gender;import java.io.Serializable;import java.util.List;/** *  @ProjectName: springmvc-mybatis  */public class Student implements Serializable{ private static final long serialVersionUID = 735655488285535299L; private String id; private String name; private int age; private Gender gender; private List<Teacher> teachers; setters&getters()....; toString(); } 二、在映射文件中开启二级缓存<?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.yihaomen.mybatis.dao.StudentMapper"> <!--开启本mapper的namespace下的二级缓存--> <!-- eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。 (1) LRU,最近起码应用的,一处最长工夫不必的对象 (2) FIFO,先进先出,按对象进入缓存的程序来移除他们 (3) SOFT,软援用,移除基于垃圾回收器状态和软援用规定的对象 (4) WEAK,弱援用,更踊跃的移除基于垃圾收集器状态和弱援用规定的对象。这里采纳的是LRU, 移除最长工夫不必的对形象 flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当 SQL被执行的时候才会去刷新缓存。 size:援用数目,一个正整数,代表缓存最多能够存储多少个对象,不宜设置过大。设置过大会导致内存溢出。 这里配置的是1024个对象 readOnly:只读,意味着缓存数据只能读取而不能批改,这样设置的益处是咱们能够疾速读取缓存,毛病是咱们没有 方法批改缓存,他的默认值是false,不容许咱们批改 --> <cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/> <resultMap id="studentMap" type="Student"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="age" column="age" /> <result property="gender" column="gender" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler" /> </resultMap> <resultMap id="collectionMap" type="Student" extends="studentMap"> <collection property="teachers" ofType="Teacher"> <id property="id" column="teach_id" /> <result property="name" column="tname"/> <result property="gender" column="tgender" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/> <result property="subject" column="tsubject" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/> <result property="degree" column="tdegree" javaType="string" jdbcType="VARCHAR"/> </collection> </resultMap> <select id="selectStudents" resultMap="collectionMap"> SELECT s.id, s.name, s.gender, t.id teach_id, t.name tname, t.gender tgender, t.subject tsubject, t.degree tdegree FROM student s LEFT JOIN stu_teach_rel str ON s.id = str.stu_id LEFT JOIN teacher t ON t.id = str.teach_id </select> <!--能够通过设置useCache来规定这个sql是否开启缓存,ture是开启,false是敞开--> <select id="selectAllStudents" resultMap="studentMap" useCache="true"> SELECT id, name, age FROM student </select> <!--刷新二级缓存 <select id="selectAllStudents" resultMap="studentMap" flushCache="true"> SELECT id, name, age FROM student </select> --></mapper>三、在 mybatis-config.xml中开启二级缓存<?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> <settings> <!--这个配置使全局的映射器(二级缓存)启用或禁用缓存--> <setting name="cacheEnabled" value="true" /> ..... </settings> ....</configuration>四、测试package com.yihaomen.service.student;import com.yihaomen.mybatis.dao.StudentMapper;import com.yihaomen.mybatis.model.Student;import com.yihaomen.mybatis.model.Teacher;import com.yihaomen.service.BaseTest;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import java.util.List;/** *    *  @ProjectName: springmvc-mybatis  */public class TestStudent extends BaseTest { public static void selectAllStudent() { SqlSessionFactory sqlSessionFactory = getSession(); SqlSession session = sqlSessionFactory.openSession(); StudentMapper mapper = session.getMapper(StudentMapper.class); List<Student> list = mapper.selectAllStudents(); System.out.println(list); System.out.println("第二次执行"); List<Student> list2 = mapper.selectAllStudents(); System.out.println(list2); session.commit(); System.out.println("二级缓存观测点"); SqlSession session2 = sqlSessionFactory.openSession(); StudentMapper mapper2 = session2.getMapper(StudentMapper.class); List<Student> list3 = mapper2.selectAllStudents(); System.out.println(list3); System.out.println("第二次执行"); List<Student> list4 = mapper2.selectAllStudents(); System.out.println(list4); session2.commit(); } public static void main(String[] args) { selectAllStudent(); }}后果: ...

August 7, 2020 · 3 min · jiezi

关于mybatis:Mybatis中的标签

官网文档:mybatis官网 mapper中的几个顶级元素:cachecache-refresultMapparameterMapsqlinsertdeleteupdateselectSELECT<select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">注:resultType与resultMap中只能二选一。 resultType能够是任意包装类型/实体类;而resultMap更加弱小。能够先看一篇文章先理解一下:[ResultType与ResultMap区别](https://blog.csdn.net/weixin_... flushCache:调用sql后,清空本地缓存和二级缓存,默认值为false。useCache:调用后二级缓存存下来,对select默认为true。statementType: PREPARED|STATEMENT|CALLABLE,别离对应相应的statement类型。 INSERT/DELETE/UPDATE<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyColumn="" useGeneratedKeys="" keyProperty="" timeout="20"><update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20"><delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">次级元素(顶级标签下的标签)foreach<foreach item="item" collection="list" separator=","> (#{item.username}, #{item.password}, #{item.email}, #{item.bio})</foreach>selectKey<selectKey keyProperty="id" resultType="int" order="BEFORE"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectKey>sql 定义可重用的代码片段定义 <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>应用 <select id="selectUsers" resultType="map"> select <include refid="userColumns"><property name="alias" value="t1"/></include>, <include refid="userColumns"><property name="alias" value="t2"/></include> from some_table t1 cross join some_table t2</select>动静sql(if,choose/when/otherwise,trim,where,set,bind)

August 1, 2020 · 1 min · jiezi

关于mybatis:Mybatis中的标签

官网文档:mybatis官网 mapper中的几个顶级元素:cachecache-refresultMapparameterMapsqlinsertdeleteupdateselectSELECT<select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">注:resultType与resultMap中只能二选一。 resultType能够是任意包装类型/实体类;而resultMap更加弱小。能够先看一篇文章先理解一下:[ResultType与ResultMap区别](https://blog.csdn.net/weixin_... flushCache:调用sql后,清空本地缓存和二级缓存,默认值为false。useCache:调用后二级缓存存下来,对select默认为true。statementType: PREPARED|STATEMENT|CALLABLE,别离对应相应的statement类型。 INSERT/DELETE/UPDATE<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyColumn="" useGeneratedKeys="" keyProperty="" timeout="20"><update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20"><delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">次级元素(顶级标签下的标签)foreach<foreach item="item" collection="list" separator=","> (#{item.username}, #{item.password}, #{item.email}, #{item.bio})</foreach>selectKey<selectKey keyProperty="id" resultType="int" order="BEFORE"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectKey>sql 定义可重用的代码片段定义 <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>应用 <select id="selectUsers" resultType="map"> select <include refid="userColumns"><property name="alias" value="t1"/></include>, <include refid="userColumns"><property name="alias" value="t2"/></include> from some_table t1 cross join some_table t2</select>动静sql(if,choose/when/otherwise,trim,where,set,bind)

August 1, 2020 · 1 min · jiezi

关于mybatis:从零开始手写-mybatis-三jdbc-pool-从零实现数据库连接池

前景回顾第一节 从零开始手写 mybatis(一)MVP 版本 中咱们实现了一个最根本的能够运行的 mybatis。 第二节 从零开始手写 mybatis(二)mybatis interceptor 插件机制详解 本节咱们一起来看一下如何实现一个数据库连接池。 为什么须要连接池?数据库连贯的创立是十分耗时的一个操作,在高并发的场景,如果每次对于数据库的拜访都从新创立的话,老本太高。 于是就有了“池化”这种解决方案。 这种计划在咱们日常生活中也是亘古未有,比方资金池,需要池,乃至人力资源池。 思维都是共通的。 咱们本节一起来从零实现一个繁难版本的数据库连接池,不过麻雀虽小,五脏俱全。 将从以下几个方面来开展: (1)一般的数据库连贯创立 (2)主动适配 jdbc 驱动 (3)指定大小的连接池创立 (4)获取连贯时增加超时检测 (5)增加对于连贯有效性的检测 一般的数据库连贯创立这种就是最一般的不实用池化的实现。 实现mybatis 默认其实也是这种实现,不过咱们在这个根底上做了一点优化,那就是能够依据 url 主动适配 driverClass。 public class UnPooledDataSource extends AbstractDataSourceConfig { @Override public Connection getConnection() throws SQLException { DriverClassUtil.loadDriverClass(super.driverClass, super.jdbcUrl); return DriverManager.getConnection(super.getJdbcUrl(), super.getUser(), super.getPassword()); }}主动适配这个个性次要是参考阿里的 druid 连接池实现,在用户没有指定驱动类时,主动适配。 外围代码如下: /** * 加载驱动类信息 * @param driverClass 驱动类 * @param url 连贯信息 * @since 1.2.0 */public static void loadDriverClass(String driverClass, final String url) { ArgUtil.notEmpty(url, url); if(StringUtil.isEmptyTrim(driverClass)) { driverClass = getDriverClassByUrl(url); } try { Class.forName(driverClass); } catch (ClassNotFoundException e) { throw new JdbcPoolException(e); }}如何依据 url 获取启动类呢?实际上就是一个 map 映射。 ...

July 18, 2020 · 5 min · jiezi

关于mybatis:从零开始手写-mybatis二mybatis-interceptor-插件机制详解

前景回顾第一节 从零开始手写 mybatis(一)MVP 版本 中咱们实现了一个最根本的能够运行的 mybatis。 常言道,万事开头难,而后两头难。 mybatis 的插件机制是 mybatis 除却动静代理之外的第二大灵魂。 上面咱们一起来体验一下这乏味的灵魂带来的苦楚与高兴~ 插件的作用在理论开发过程中,咱们常常应用的Mybaits插件就是分页插件了,通过分页插件咱们能够在不必写count语句和limit的状况下就能够获取分页后的数据,给咱们开发带来很大 的便当。除了分页,插件应用场景次要还有更新数据库的通用字段,分库分表,加解密等的解决。 这篇博客次要讲Mybatis插件原理,下一篇博客会设计一个Mybatis插件实现的性能就是每当新增数据的时候不必数据库自增ID而是通过该插件生成雪花ID,作为每条数据的主键。 JDK动静代理+责任链设计模式Mybatis的插件其实就是个拦截器性能。它利用JDK动静代理和责任链设计模式的综合使用。采纳责任链模式,通过动静代理组织多个拦截器,通过这些拦截器你能够做一些你想做的事。 所以在讲Mybatis拦截器之前咱们先说说JDK动静代理+责任链设计模式。 JDK 动静代理案例package com.github.houbb.mybatis.plugin;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class JdkDynamicProxy { /** * 一个接口 */ public interface HelloService{ void sayHello(); } /** * 指标类实现接口 */ static class HelloServiceImpl implements HelloService{ @Override public void sayHello() { System.out.println("sayHello......"); } } /** * 自定义代理类须要实现InvocationHandler接口 */ static class HelloInvocationHandler implements InvocationHandler { private Object target; public HelloInvocationHandler(Object target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("------插入前置告诉代码-------------"); //执行相应的指标办法 Object rs = method.invoke(target,args); System.out.println("------插入后置解决代码-------------"); return rs; } public static Object wrap(Object target) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new HelloInvocationHandler(target)); } } public static void main(String[] args) { HelloService proxyService = (HelloService) HelloInvocationHandler.wrap(new HelloServiceImpl()); proxyService.sayHello(); }}输入------插入前置告诉代码-------------sayHello......------插入后置解决代码-------------优化1:面向对象下面代理的性能是实现了,然而有个很显著的缺点,就是 HelloInvocationHandler 是动静代理类,也能够了解成是个工具类,咱们不可能会把业务代码写到写到到invoke办法里, ...

July 18, 2020 · 6 min · jiezi

从零开始手写-mybatis一MVP-版本

什么是 MyBatis ?MyBatis 是一款优良的长久层框架,它反对定制化 SQL、存储过程以及高级映射。 MyBatis 防止了简直所有的 JDBC 代码和手动设置参数以及获取后果集。 MyBatis 能够应用简略的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,一般的 Java对象)映射成数据库中的记录。(这是官网解释) MyBatis 运行原理 当框架启动时,通过configuration解析config.xml配置文件和mapper.xml映射文件,映射文件能够应用xml形式或者注解形式,而后由configuration取得sqlsessionfactory对象,再由sqlsessionfactory取得sqlsession数据库拜访会话对象,通过会话对象取得对应DAO层的mapper对象,通过调用mapper对象相应办法,框架就会主动执行SQL语句从而取得后果。 手写 mybatis其实整体流程就是这么简略,咱们来一起实现一个简略版本的 mybatis。 创作目标(1)深刻学习 mybatis 的原理 一千个读者就有一千个哈姆雷特,一千个作者就有一千个莎士比亚。——老马 (2)实现属于本人的 mybatis 工具。 数据库的品种实际上有几百种,比方工作中就用到过 GreenPlum 这种绝对小众的数据库,这时候 mybatis 可能就不能应用了。 感觉大可不必,合乎 SQL 规范都应该对立反对下,这样更加不便实用。 实现形式本系列目前共计 17 个迭代版本,根本实现了 mybatis 的外围个性。 耗时大略十天左右,绝对实现的形式比较简单。 采纳 mvp 的开发策略,逐步增加新的个性。 本系列将对外围代码进行解说,残缺代码曾经全副开源 https://github.com/houbb/mybatis疾速体验mysql 装置不是本系列重点,请自行找材料。 版本:应用的是 v5.7 版本,v8.0 之后依赖的驱动包会有所不同。 sql 执行-- auto-generated definitioncreate table user( id int auto_increment primary key, name varchar(100) not null, password varchar(100) not null);insert into user (name, password) value ('ryo', '123456');maven 引入<dependency> <groupId>com.github.houbb</groupId> <artifactId>mybatis</artifactId> <version>0.0.1</version></dependency>配置文件mybatis-config-5-7.xml<?xml version="1.0" encoding="UTF-8"?><configuration> <dataSource> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers></configuration>测试代码Config config = new XmlConfig("mybatis-config-5-7.xml");SqlSession sqlSession = new DefaultSessionFactory(config).openSession();UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.selectById(1L);System.out.println(user);输入后果: ...

July 13, 2020 · 5 min · jiezi

Mybatis批量删除功能实现

我是阿福,公众号「阿福聊编程」作者,一个在后端技术路上摸盘滚打的程序员,在进阶的路上,共勉!文章已收录在 JavaSharing 中,蕴含Java技术文章,面试指南,资源分享。前台实现表单实现首先定义全选框的的id属性id="summaryBox" <th width="30"><input id="summaryBox" type="checkbox"></th>而后定义一个数据单选框的class属性 class="itemBox",阐明:adminId属性是HTML标签自身并没有的属性,是咱们强行设置的。 <th width="30"><input adminId="${item.id}" class="itemBox" type="checkbox"></th>整个表单文件 <table class="table table-bordered"> <thead> <tr> <th width="30">#</th> <th width="30"><input id="summaryBox" type="checkbox"></th> <th>账号</th> <th>名称</th> <th>邮箱地址</th> <th width="100">操作</th> </tr> </thead> <tbody> <c:if test="${empty requestScope['PAGEINFO-ADMIN'].list}"> <tr> <td style="text-align: center" colspan="6">道歉,没有用户查问的数据!!!!</td> </tr> </c:if> <c:if test="${! empty requestScope['PAGEINFO-ADMIN'].list}"> <c:forEach items="${requestScope['PAGEINFO-ADMIN'].list}" var="item" varStatus="myStatus"> <tr> <td>${myStatus.count}</td> <th width="30"><input adminId="${item.id}" class="itemBox" type="checkbox"></th> <td>${item.loginAcct}</td> <td>${item.userName}</td> <td>${item.email}</td> <td> <button type="button" class="btn btn-success btn-xs"><i class=" glyphicon glyphicon-check"></i></button> <a href="admin/toupdateadmin.action?adminId=${item.id}" type="button" class="btn btn-primary btn-xs"><i class=" glyphicon glyphicon-pencil"></i></a> <button type="button" adminId="${item.id}" class="btn btn-danger btn-xs uniqueRemoveBtn"><i class=" glyphicon glyphicon-remove"></i></button> </td> </tr> </c:forEach> </c:if> </tbody> <tfoot> <tr> <td colspan="6" align="center"> <div id="Pagination" class="pagination"><!-- 这里显示分页 --></div> </td> </tr> </tfoot> </table>jQuery代码 // 全选/全不选性能 $("#summaryBox").click(function () { $(".itemBox").prop("checked", this.checked); });前后台交互jQuery代码实现给批量删除按钮标记id ...

July 11, 2020 · 3 min · jiezi

mybatis异常集之Cannot-determine-value-type-from-string-‘xxx‘

前言本文的创作来源于朋友在自学mybatis遇到的问题,问题如文章标题所示Cannot determine value type from string 'xxx'。他在网上搜索出来的答案基本上都是加上一个无参构造器,就可以解决问题。他的疑问点在于他实体没有使用无参构造器,而使用了有参构造器,有的查询方法不会报错,有的查询方法却报错了。下面将演示他出现的这种场景的示例。 注: mybatis的搭建过程忽略,仅演示案例。案例代码取自朋友 示例1、entitypublic class Student { private int id; private String name; private String email; private int age; public Student(String aa,int bb){ System.out.println("===============执行student的有参数构造方法 aa = "+aa+" bb = "+bb+"================"); } 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 getEmail() { return email; } public void setEmail(String email) { this.email = email; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", email='" + email + '\'' + ", age=" + age + '}'; }}2、daopublic interface StudentDao { Student getStudentById(int id); List<Student> getStudents(@Param("myname") String name, @Param("myage") int age); List<Student> getStudentByObj(Student student);}3、mapper.xml ...

July 3, 2020 · 4 min · jiezi

MyBatis-配置详解

MyBatis的全局配置文件SqlMapConfig.xml是mybatis的全局配置文件:properties(属性) settings(全局配置参数) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境集合属性对象) environment(环境子属性对象) transactionManager(事务管理) dataSource(数据源) mappers(映射器) properties将数据库连接参数单独配置在db.properties中,放在类路径下。这样只需要在SqlMapConfig.xml中加载db.properties的属性值。这样在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。 将数据库连接参数只配置在db.properties中,原因:方便对参数进行统一管理,其它xml可以引用该db.properties。 使用示例: db.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=root 相应的SqlMapConfig.xml <properties resource="db.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <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> </properties> 注意: MyBatis 将按照下面的顺序来加载属性: 首先在properties标签中指定的属性文件首先被读取。 然后会读取properties元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。 常用做法: 不要在properties元素体内添加任何属性值,只将属性值定义在外部properties文件中。 在properties文件中定义属性名要有一定的特殊性,如:xxx.xxx.xxx的形式,就像jdbc.driver。这样可以防止和parameterType传递的属性名冲突,从而被覆盖掉。 settingsmybatis全局配置参数,全局参数将会影响mybatis的运行行为。比如:开启二级缓存、开启延迟加载。具体可配置情况如下: 配置示例: <settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> </settings> typeAliasestypeAliases可以用来自定义别名。在mapper.xml中,定义很多的statement,而statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。 例如: <typeAliases> <!-- 单个别名定义 --> <typeAlias alias="user" type="com.test.model.User"/> <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大小写都可以) --> <package name="com.test.model"/> <package name="其它包"/> </typeAliases> mapper.xml的引用形式 ...

June 24, 2020 · 2 min · jiezi

PageHelper源码详解

PageHelper插件相关注解说明PageHelperAutoConfiguration类的相关注解说明: @Configuration:相当于<beans></beans>标签@ConditionalOnBean(SqlSessionFactory.class):只有在上下文中存在SqlSessionFactory的bean是才会运行PageHelperAutoConfiguration@EnableConfigurationProperties(PageHelperProperties.class): @ConfigurationProperties注解主要用来把properties配置文件转化为bean来使用的,而@EnableConfigurationProperties注解的作用是@ConfigurationProperties注解生效。如果只配置@ConfigurationProperties注解,在IOC容器中是获取不到properties配置文件转化的bean的。@ConfigurationProperties(prefix = PageHelperProperties.PAGEHELPER_PREFIX):PageHelperProperties是PageHelper的配置类,将properties中的配置文件转换成PageHelperProperties对象,PageHelperProperties上有@ConfigurationProperties(prefix = "PageHelperProperties.PAGEHELPER_PREFIX")注解。@AutoConfigureAfter(MybatisAutoConfiguration.class):当MybatisAutoConfiguration这个类加载完成后再加载本类@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。执行顺序:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)PageInterceptor类的相关注解说明 @Intercepts({@Signature( type \= Executor.class, method \= "query", args \= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} ), @Signature( type \= Executor.class, method \= "query", args \= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class} )})type:表示拦截的类,这里是Excutor的实现类method:表示拦截的方法,这里拦截的是Executor的query方法args:表示方法参数 Mybatis拦截器原理分析Mybatis提供的插件(Plugin)功能其实就是拦截器功能,默认情况下,Mybatis允许使用插件来拦截方法的调用包括一下几种: 1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) //拦截执行器的方法 2. ParameterHandler (getParameterObject, setParameters) //拦截参数的方法 3. ResultSetHandler (handleResultSets, handleOutputParameters) //拦截结果集的方法 4. StatementHandler (prepare, parameterize, batch, update, query) //拦截Sql语法构建的处理PageHelper自动配置pageHelper的自动配置类名:PageHelperAutoConfiguration ...

June 2, 2020 · 4 min · jiezi

spring中使用mybatis处理select时传入List参数

1. 使用List<Map.Entry<String, String>> params作为传入参数service层: public List<Student> getMultiStudentInMap(List<Map.Entry<String, String>> params, long percentage) { System.out.println(params); return studentMapper.selectForeachEntry(params, percentage); }mapper.java public List<Student> selectForeachEntry(@Param("params") List<Map.Entry<String, String>> params, @Param("percentage") long percentage);mapper.xml <!-- 在foreach中,传入参数为Map或Map.Entry对象时,index是键,item是值 --> <select id="selectForeachEntry" resultMap="studentResult"> select * from test_student t where t.percentage = #{percentage} <if test="params.size() > 0"> and ( <foreach item="param" index="index" collection="params" separator=" or "> (t.name = #{index} and t.branch = #{param}) </foreach> ) </if> </select>调用方法: List<Map.Entry<String, String>> idList = new ArrayList<>(); Map<String, String> map = new TreeMap<>(); map.put("name_1", "a"); map.put("name_2", "b"); map.put("name_3", "c"); Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); while (it.hasNext()) { idList.add(it.next()); } List<Student> students = studentDao.getMultiStudentInMap(idList, 0);mybatis生成的结果:==> Preparing: select * from test_student t where t.percentage = ? and ( (t.name = ? and t.branch = ?) or (t.name = ? and t.branch = ?) or (t.name = ? and t.branch = ?) ) ==> Parameters: 0(Long), name_1(String), a(String), name_2(String), b(String), name_3(String), c(String)<== Total: 0缺点:使用Map生成Map.Entry有局限性,key值不能重复。不使用Map,就需要自己实现Map.Entry接口: class MyEntry implements Map.Entry<String, String> {...} ...

June 1, 2020 · 2 min · jiezi

Mybatis映射器动态代理实现分析

mybatis核心组件介绍SqlSessionFactoryBuilder(构造器):它可以通过xml,注解或者手动配置来创建SqlSessionFacotrySqlSessionFactory:用来创建SqlSession(会话)的工厂SqlSession:SqlSession是mybatis最核心的类,可以用来执行语句,提交或者回滚事务以及获取映射器Mapper的接口SQL Mapper:它是由一个接口,一个xml配置文件或者注解构成,需要给出对应的SQL和映射规则,它负责发送SQL去执行,并发挥结果组件使用案例: public class MybatisTest { private static SqlSessionFactory sqlSessionFactory; static { try { sqlSessionFactory = new SqlSessionFactoryBuilder() .build(Resources.getResourceAsStream("mybatis-config.xml")); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.selectById(1); System.out.println("User : " + user); } }}// 结果:User : User{id=1, age=21, name='pjmike'}mybatis动态代理实现public static void main(String[] args) { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// <1> User user = userMapper.selectById(1); System.out.println("User : " + user); }} 在前面的例子中,我们使用sqlSession的getMapper方法获取了UserMapper对象,实际上我们获取的是UserMapper接口的代理类,然后由代理类来执行方法。在探索动态代理类实现之前,我们需要先明确sqlSessionFactory工厂的创建过程做了哪些准备工作。 ...

May 29, 2020 · 5 min · jiezi

springboot-mybatis报错-Invalid-bound-statement-not-found

1.问题描述新建一个springboot项目集成mybatis,mapper.xml文件放在resource下的mybatis.mapper目录下:启动项项目,执行xml里的查询sql报错:sql未绑定。 2.调试跟踪可以启动项目时debug发现mapper.xml加载不到。各种对比和跟踪源码发现,当mapper-locations设置为classpath:mybatis.mapper/*Mapper.xml时程序正常。 3.结果结果发现是resource下的目录文件建立有问题。建的并不是二级目录文件夹,而是一个名为"mybatis.mapper"的一级文件。通过重新建一个mybatis文件夹再建一个mapper子文件夹结果问题。

May 26, 2020 · 1 min · jiezi