mybatis基础学习
JDBC Mysql java基础 maven Junit
一、简介
1. 什么是mybatis
- MyBatis 是一款优秀的持久层框架;
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
如何获得mybatis?
maven仓库:Maven Repository: org.mybatis » mybatis
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.16</version>
</dependency>
GitHub:Releases · mybatis/mybatis-3
中文文档:MyBatis 3 | 入门 – mybatis
获得mybatis:
maven仓库:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency>
2. 持久化
主要是做数据持久化。
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程。
- 内存特性:断电即失
- 数据库(Jdbc)和io文件可以持久化
为什么需要持久化:有些对象不能丢掉;内存太贵;
3. 持久层
Dao层、 Service层、 Controller层
Service层做业务操作,业务层调Dao层,controller接收用户请求,并把请求转化给业务去做,
- 完成持久化工作的代码块
- 层界限十分明显
4. 为什么需要Mybatis?
帮助程序员将数据存入到数据库中;
方便,简化传统的复杂的JDBC代码,实现自动化;
优点:
-
简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。易于学习,易于使用。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
-
灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
-
解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
-
提供映射标签,支持对象与数据库的ORM字段关系映射。
-
提供对象关系映射标签,支持对象关系组建维护。
-
提供xml标签,支持编写动态sql。
使用的人多。
二、第一个Mybatis程序
思路:搭建环境-->导入Mybatis-->编写代码-->测试
1. 搭建数据库
create database `mybatis`;use `mybatis`;create table `user`(`id` int(20) not null primary key,`name` varchar(30) default null,`pwd` varchar(30) default null
)engine=innodb default charset=utf8;insert into `user` (`id`, `name`, `pwd`) values
(1, '狂神', '123456'),
(2 ,'张三', '123456'),
(3, '李四', '123890');
新建项目:
- 新建一个普通的maven项目(setting中查看maven版本和仓库地址)
- 删除src目录(作为父工程),再在父工程下建子模块
- 导入maven依赖
<dependencies><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.31</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency><!--Junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency>
</dependencies>
父文件与子文件的pom文件中都加入(原因下面第4部分讲):
<build><resources><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources>
</build>
2. 创建模块
编写mybatis的核心配置文件
<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://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.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="mysq123"/></dataSource></environment></environments><!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册--><mappers><mapper resource="com/wlp/dao/UserMapper.xml"/></mappers>
</configuration>
编写mybatis工具类
public class MybatisUtils {private static SqlSessionFactory sqlSessionFactory;static {try {
// 使用mybatis第一步:获取sqlSessionFactory对象String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (Exception e) {e.printStackTrace();}}
// 通过sqlSessionFactory获得SqlSession实例。
// SqlSession完全包含了面向数据库执行SQL命令所需的所有方法。public static SqlSession getSqlSession(){return sqlSessionFactory.openSession();}
}
3. 编写代码
① 实体类
跟数据库中字段名一样。构造器和get、set方法可以以@Lombok注解替代
② Dao接口(Dao等价于以后的Mapper)
public interface UserDao {List<User> getUserList();
}
③ 接口实现类:由原来的UserDaoImpl转换为一个mapper配置文件
<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定一个对应的mapper接口-->
<mapper namespace="com.wlp.dao.UserDao"><select id="getUserList" resultType="com.wlp.pojo.User">select * from mybatis.user</select>
</mapper>
4. 测试
注意:当报错:org.apache.ibatis.binding.BindingException:Type interface com.wlp.dao.UserDao is not known to the Mapper Registry. 时,需要在mybaits-config.xml核心配置文件中注册Mapper。
Mapper Registry 即在核心配置文件中注册mapper。
即:
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册--><mappers><mapper resource="com/wlp/dao/UserMapper.xml"/></mappers>
再报错:找不到以上xml 文件,可以看到target下面的class也并没有以上UserMapper.xml文件,这涉及的是资源过滤问题。maven由于约定大于配置,当遇到自己写的配置文件无法被导出或者生效的问题,解决方案如下:
<build><resources><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources>
</build>
Junit测试
public class UserDaoTest {@Testpublic void test(){
// 第一步:获得sqlSession对象SqlSession sqlSession = mybatisUtils.getSqlSession();
// 执行sql
// 方式一:getMapperUserDao userDao = sqlSession.getMapper(UserDao.class);List<User> userList = userDao.getUserList();for (User user : userList) {System.out.println(user);}
// 方式二:List<User> userList1 = sqlSession.selectList("com.wlp.dao.UserDao.getUserList");for (User user : userList1) {System.out.println(user);}// 关闭sqlSessionsqlSession.close();}
}
mybatis三个核心接口:(笔试)
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {// 你的应用逻辑代码
}
在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。(实践中用处不大)
UserDao文件名改为UserMapper:
三、CRUD
1. namespace
namespace中的包名要和Dao/Mapper接口的包名一致
2. select、insert、update、delete
选择,查询语句
- id:就是对应的namespace中的接口的方法名;
- resultType:sql语句执行的返回值;
- parameterType:参数类型;
操作步骤:
(1)编写接口
// 查询全部用户List<User> getUserList();
// 根据ID查询用户User getUserById(int id);
// 增添用户int addUser(User user);
// 删除int deleteUser(int id);
// 修改int updateUser(User user);
(2)编写对应的mapper中的sql
<!-- 查询全部用户--><select id="getUserList" resultType="com.wlp.pojo.User">select * from mybatis.user</select>
<!-- 根据用户ID查询用户--><select id="getUserById" parameterType="int" resultType="com.wlp.pojo.User">select * from mybatis.user where id = #{id}</select>
<!-- 增添用户--><insert id="addUser" parameterType="com.wlp.pojo.User">insert into mybatis.user values(#{id},#{name},#{pwd})</insert>
<!-- 删除用户--><delete id="deleteUser" parameterType="int" >delete from mybatis.user where id = #{id}</delete>
<!-- 修改用户--><update id="updateUser" parameterType="com.wlp.pojo.User">update mybatis.user set name = #{name}, pwd = #{pwd} where id = #{id}</update>
(3)测试
@Test//查询全部用户public void test(){
// 第一步:获得sqlSession对象SqlSession sqlSession = mybatisUtils.getSqlSession();
// 执行sql
// 方式一:getMapperUserMapper userDao = sqlSession.getMapper(UserMapper.class);List<User> userList = userDao.getUserList();for (User user : userList) {System.out.println(user);}System.out.println("==============================");// 方式二:List<User> userList1 = sqlSession.selectList("com.wlp.dao.UserMapper.getUserList");for (User user : userList1) {System.out.println(user);}// 关闭sqlSessionsqlSession.close();}@Test//根据id查询用户public void getUserById(){SqlSession sqlSession = mybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User userById = mapper.getUserById(2);System.out.println(userById);sqlSession.close();}@Test//添加用户public void addUser(){SqlSession sqlSession = mybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);int user4 = mapper.addUser(new User(4, "哈哈哈", "23edjf"));List<User> userlist = mapper.getUserList();for (User user : userlist) {System.out.println(user);}sqlSession.commit();sqlSession.close();}@Test//删除用户public void deleteUser(){SqlSession sqlSession = mybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);int i = mapper.deleteUser(3);List<User> userList3 = mapper.getUserList();for (User user : userList3) {System.out.println(user);}sqlSession.commit();sqlSession.close();}@Test//修改用户public void updateUser(){SqlSession sqlSession = mybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);int i = mapper.updateUser(new User(2, "张四", "zhangsi"));List<User> userList = mapper.getUserList();for (User user : userList) {System.out.println(user);}sqlSession.commit();sqlSession.close();}
注意:增删改要提交事务
3. Map传参
当数据库中表字段或参数过多,考虑使用Map.
// 用map添加int addUser2(Map<String, Object> map);
// 用map查询User getUserBy2(Map<String, Object> map);
<insert id="addUser2" parameterType="map">insert into mybatis.user(id, pwd) values(#{userId},#{userPwd})
</insert>
<select id="getUserBy2" parameterType="map" resultType="com.wlp.pojo.User">select * from mybatis.user where name = #{userName} and pwd = #{userPwd}
</select>
参数定位符的名称和map中的key需保持一致,而不必和数据库字段名一致。
@Test
public void addUser2(){SqlSession sqlSession = mybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);Map<String, Object> map = new HashMap<String, Object>();map.put("userId", 6);map.put("userPwd", "666666");int i = mapper.addUser2(map);List<User> userList = mapper.getUserList();for (User user : userList) {System.out.println(user);}sqlSession.commit();sqlSession.close();
}
@Test
public void getUserList2(){SqlSession sqlSession = mybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);Map<String, Object> map = new HashMap<String, Object>();map.put("userName", "狂神");map.put("userPwd", "123456");User userList2 = mapper.getUserBy2(map);System.out.println(userList2);sqlSession.close();
}
- Map传递参数,直接在sql中取出key即可!【parameterType="map"】
- 对象传递参数,直接在sql中取对象的属性即可!【parameterType="Object"】例:parameterType="User"
- 只有一个基本类型参数的情况下,可以直接在sql中取到!例如int类型
- 多个参数用Map,或者注解。
4. 模糊查询
(1)java代码执行的时候,传递通配符%%
List<User> userByLike = mapper.getUserByLike("%张%");
(2)在sql拼接中使用通配符
select * from mybatis.user where name like "%"#{value}"%"
完整示例:
// 模糊查询List<User> getUserByLike(String name);
<select id="getUserByLike" parameterType="String" resultType="com.wlp.pojo.User">select * from mybatis.user where name like "%"#{value}"%"
</select>
@Testpublic void getUserByLike(){SqlSession sqlSession = mybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// List<User> userByLike = mapper.getUserByLike("%张%");List<User> userByLike = mapper.getUserByLike("张");for (User user : userByLike) {System.out.println(user);}sqlSession.close();}
四、配置解析
1. 核心配置文件
mybatis-config.xml
MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息。
- configuration(配置)
- properties(属性)√
- settings(设置)√
- typeAliases(类型别名)√√
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)√
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)√
2. 环境配置(environments)
mybatis 可以配置成适应多种环境,但尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境。
mybatis默认的事务管理器就是JDBC,默认的数据库连接池:POOLED
3. 属性(settings)
可以通过properties属性来实现应用配置文件。
这些属性可以在外部进行配置,并可以进行动态替换。既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
编写一个配置文件:db.properties 放在resources目录下
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
user=root
password=mysq123
driver=com.mysql.cj.jdbc.Driver
在核心配置文件中引入
<!-- 引入外部配置文件,properties也是自闭合的,property可以不用写,全部写在db.properties配置文件中即可-->
<properties resource="db.properties"><property name="user" value="root"/><property name="pwd" value="mysq123"/>
</properties>
<environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${user}"/><property name="password" value="${pwd}"/> 优先使用外部配置文件中配置的,而不是properties标签中设置的</dataSource></environment>
</environments>
- 可以直接引入外部文件:db.properties
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件(db.properties)的。
4. 类型别名(typeAlias)
类型别名是为java类型设置的一个短的名字。存在的意义仅在于用来减少类完全限定名的冗余。
方式1:
<typeAliases><typeAlias type="com.wlp.pojo.User" alias="User"></typeAlias>
</typeAliases>
resultType的属性值由 “com.wlp.pojo.User ” 变为 "User"
<select id="getUserList" resultType="User">select * from mybatis.user
</select>
方式2:也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
扫描实体类的包,它的默认别名就为这个类的类名,首字母小写!
<typeAliases><package name="com.wlp.pojo"/>
</typeAliases>
<select id="getUserList" resultType="user">select * from mybatis.user
</select>
- 在实体类比较少的时候,使用第一种方式。
- 当实体类非常多使用第二种。
- 第一种可以DIY别名,第二种不行。如果非要给第二种DIY别名,需要在实体上增加注释
@Alias("hellouser")
public Class User{}
5. 设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
6. 映射器(mappers)
MapperRegistry: 注册绑定Mapper文件;
方式一:使用相对于类路径的资源引用【推荐使用】(使用xml写SQL时)
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers><mapper resource="com/wlp/dao/UserMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册(使用注解写SQL时)
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers><mapper class="com.wlp.dao.UserMapper"/>
</mappers>
方式三:使用扫描包进行注入绑定
<mappers><package name="com.wlp.dao"/>
</mappers>
方式二和方式三注意点:接口和它的Mapper配置文件必须同名且必须在同一个包下
总结:
- 将数据库配置文件外部引入;
- 实体类别名;
- 保证UserMapper接口和UserMapper.xml改为一致!并且放在同一个包下!
7. 生命周期和作用域
生命周期和作用域,错误使用会导致很严重的并发问题。
SqlSessionFactoryBuilder:
一旦创建了SqlSessionFactory,就不再需要它了,局部变量
SqlSessionFactory:
可以想象为:数据库连接池
SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有理由丢弃或创建另一个实例
因此 SqlSessionFactory 的最佳作用域是应用作用域。
最简单的就是使用单例模式或者静态单例模式。
SqlSession:
连接到连接池的一个请求!
SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳作用域是请求或方法作用域
用完之后需要赶紧关闭,否则资源被占用!
这里面的每一个mapper,就代表一个具体的业务。
五、ResultMap
主要用来解决属性名和字段不一致的问题
数据库中的字段:
新建一个项目,copy之前的,测试实体类字段不一致的情况。
public class User {public int id;public String name;public String password;
}
运行结果:
1. 起别名方式
解决方式:给名称不一致的字段起别名
<select id="getUserList" resultType="user">select id, name, pwd as password from mybatis.user
</select>
2. ResultMap方式
数据库中:id name pwd
实体类中:id name password
<!-- 结果集映射--><resultMap id="userMap" type="User"><result column="pwd" property="password"></result></resultMap>
<!-- 查询全部用户--><select id="getUserList" resultMap="userMap">select * from mybatis.user</select>
<select>中 resultMap 的属性与 <resultMap> 中 id 的属性一致
<resultMap> 中type的属性值为原来 <select> 中 resultType的属性值
<result column="pwd" property="password"> 中column属性为数据库中字段名,property属性为Java实体类中声明的属性名。
- resultMap 元素是MyBatis 中最重要最强大的元素。
- ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述他们的关系就行。
六、日志
1. 日志工厂
如果数据库操作出现了异常,可以借助日志排错。
- SLF4J
- LOG4J(3.5.9 起废弃) 【※】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【※】
- NO_LOGGING
在mybatis中具体使用哪一个日志实现,在设置中实现。
<settings><!--标准的日志工厂实现--><setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
进行了以上配置,控制台即可输出日志,不需要额外的依赖和配置。
2. Log4j
是Apache的一个开源项目,通过使用 Log4j ,可以控制日志信息输送的目的地是控制台、文件、GUI组件;
也可以控制每一条日志的输出格式;
通过定义每一条日志信息的级别,能够更加细致地控制日志的生成过程;
通过配置文件来灵活地进行配置,而不需要修改应用的代码。
(1)首先导入 log4j 的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
(2)log4j.properties
#将等级为DEBUG的日志信息输出到console和file两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/wlp.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
(3)配置log4j为日志的实现
<settings><setting name="logImpl" value="LOG4J"/>
</settings>
(4)Log4j的使用
在要使用Log4j的类中,导入包import org.apache.log4j.Logger;
日志对象,参数为当前类的class:
static Logger logger = Logger.getLogger(UserDaoTest.class);
日志级别
public void testLog4j(){logger.info("info:进入了testLog4j");logger.debug("debug:进入了testLog4j");logger.error("error:进入了testLog4j");
}
七、分页
IDEA中写sql
1. 使用limit分页
// 分页查询List<User> getByLimit(Map<String, Integer> map);
<resultMap id="userMap" type="User"><result column="pwd" property="password"></result>
</resultMap><!-- 分页查询-->
<select id="getByLimit" parameterType="map" resultMap="userMap">select * from mybatis.user limit #{startIndex}, #{pageSize}
</select>
@Test
public void getByLimit(){SqlSession sqlSession = mybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);Map<String, Integer> map = new HashMap<String, Integer>();map.put("startIndex", 2);map.put("pageSize", 3);List<User> byLimit = mapper.getByLimit(map);for (User user : byLimit) {System.out.println(user);}sqlSession.close();
}
2. RowBounds分页
不再使用SQL实现分页。
xml文件中写的sql不传有关分页的参数,查的是全部数据
RowBounds rowBounds = new RowBounds(offset, limit);sqlSessioin.selectList("接口全限定名.方法名()", null, rowBounds);
得到的是分页后的全部数据
3. 分页插件
如何使用分页插件
八、使用注解开发
1. 面向接口编程
2. 使用注解开发
(1)注解在接口上实现
//使用注解开发@Select("select * from mybatis.user")List<User> getUserList();
(2)需要在核心配置文件中绑定接口
<mappers><mapper class="com.wlp.dao.UserMapper"/>
</mappers>
(3)测试使用
@Test
public void getUserList(){SqlSession sqlSession = mybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> userList = mapper.getUserList();for (User user : userList) {System.out.println(user);}sqlSession.close();
}
本质:反射机制实现
底层:动态代理
mybatis的执行流程:
(1)Resources获取加载全局配置文件
(2)实例化SqlSessionFactoryBuilder构造器
(3)解析配置文件流XMLConfigBuilder
(4)Configuration所有的配置信息
(5)SqlSessionFactory实例化
(6)transactional事务管理
(7)创建executor执行器:事务、缓存
(8)创建SqlSession
(9)实现CRUD
(10)查看是否执行成功
(11)提交事务
(12)关闭
3. CRUD
可以在工具类创建的时候实现自动提交事务,不用再手写commit :
public static SqlSession getSqlSession(){return sqlSessionFactory.openSession(true);
}
关于@Param()注解:
- 基本类型的参数或者String类型,需要加上;引用类型不需要加
- 如果只有一个基本类型的话可以忽略,但是建议加上;多个参数必须加上
- 在sql中引用的就是@Param("")中设定的属性名。
// 增@Insert("insert into mybatis.user(id, name, pwd) values (#{id}, #{name}, #{pwd})")int addUser(User user);
// 删@Delete("delete from mybatis.user where id = #{id}")int deleteUser(int id);
// 改@Update("update mybatis.user set name = #{uname} where id = #{uid}")int newUser(@Param("uname") String name, @Param("uid") int id);
// 查(根据条件)@Select("select id, name, pwd as password from mybatis.user where name = #{name}")User getBy(String name);
测试:必须将接口注册绑定到核心配置文件中。
九、Lombok的使用
- 安装Lombok插件
- 在项目中导入lombok的jar包
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version><scope>provided</scope>
</dependency>
在实体类上加注解
@Data
@Getter
@Setter
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
@Log4j
@Data 代替:无参构造,get,set,toString,hashCode,equals
十、多对一处理
数据库准备:
create table `teacher`(`id` int(10) not null,`name` varchar(30) default null,primary key (`id`)
) engine=innodb default charset = utf8;insert into teacher values (1, '秦老师');create table `student`(`id` int(10) not null,`name` varchar(30) default null,`tid` int(10) default null,primary key (`id`),key `fktid` (`tid`),constraint `fktid` foreign key (`tid`) references `teacher` (`id`)
) engine=innodb default charset = utf8;insert into `student` values
(1, '小明','1'),
(2, '小红','1'),
(3, '小张','1'),
(4, '小李','1'),
(5, '小王','1');
测试环境搭建:
(1)导入lombok
(2)新建实体类Teacher,Student (Student类中要建立与Teacher的关系)
@Data
public class Student {private int id;private String name;private Teacher teacher;
}
@Data
public class Teacher {private int id;private String name;
}
(3)建立两个类的Mapper.java接口
(4)建立两个实体类的Mapper.xml文件
<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wlp.dao.TeacherMapper"></mapper>
(5)在核心配置文件中绑定注册Mapper接口或文件
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers><mapper class="com.wlp.dao.TeacherMapper"/><mapper class="com.wlp.dao.StudentMapper"/>
</mappers>
(6)测试查询
按照查询嵌套处理
- 实体类
- 学生类Mapper接口
- StudentMapper.xml
StudentMapper.xml中设置:
<!-- 思路: 1. 查询所有的学生信息,以及对应的老师的信息! 2. 根据查询出来的学生的tid,寻找对应的老师! 子查询 --> <select id="getStudent" resultMap="StudentTeacher"> select * from student </select> <resultMap id="StudentTeacher" type="Student"> <result property="id" column="id"></result> <result property="name" column="name"></result> <!-- 复杂的属性,需要单独处理 对象:association 集合:collection --> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="Teacher"> select * from teacher where id = #{id} </select>
按照结果嵌套处理
<!-- 按结果嵌套查询--> <select id="getStudent2" resultMap="StudentTeacher2"> select s.id sid, s.name sname, t.name tname from student s, teacher t where s.tid = t.id; </select> <resultMap id="StudentTeacher2" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
一对多处理:
创建实体类:
package com.wlp.pojo; import lombok.Data; @Data public class Student { private int id; private String name; private int tid; }
package com.wlp.pojo; import lombok.Data; import java.util.List; @Data public class Teacher { private int id; private String name; private List<Student> student; }
// 获取指定老师下的所有学生及老师的信息 Teacher getTeacher(@Param("tid") int id); Teacher getTeacher2(@Param("tid") int id);
TeacherMapper.xml中设置:
<select id="getTeacher" resultMap="TeacherStudent"> select s.id sid, s.name sname, t.name tname, t.id tid from teacher t, student s where t.id = s.tid and t.id = #{tid} </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <collection property="student" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap> <!-- =====================================--> <select id="getTeacher2" resultMap="TeacherStudent2"> select * from teacher where id = #{tid} </select> <resultMap id="TeacherStudent2" type="Teacher"> <collection property="student" javaType="ArrayList" ofType="Student" select="getStudentByTeacher" column="id"/> </resultMap> <select id="getStudentByTeacher" resultType="Student"> select * from student where tid = #{tid} </select>
总结:
- 关联 - association 【多对一】
- 集合 - collection 【一对多】
- javaType & ofType
- 注意点:
面试高频
12、动态SQL
动态SQL就是根据不同的条件生成不同的SQL语句
生成随机序号:
public class IDutils { public static String getId(){ return UUID.randomUUID().toString().replaceAll("-", ""); } @Test public void test(){ System.out.println(IDutils.getId()); } }
搭建环境
CREATE TABLE `blog`( `id` VARCHAR(50) NOT NULL COMMENT '博客id', `title` VARCHAR(100) NOT NULL COMMENT '博客标题', `author` VARCHAR(30) NOT NULL COMMENT '博客作者', `create_time` DATETIME NOT NULL COMMENT '创建时间', `views` INT(30) NOT NULL COMMENT '浏览量' )ENGINE=INNODB DEFAULT CHARSET=utf8
创建一个基础工程:
<settings> <!-- 是否开启自动驼峰命名规则映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
package com.wlp.pojo; import java.util.Date; @Data public class Blog { private String id; private String title; private String author; private Date createTime; private int views;
public interface BlogMapper { int addBlog(Blog blog); List<Blog> getBlog(); }
<insert id="addBlog" parameterType="blog"> insert into mybatis.blog(id, title, author, create_time, views) values (#{id},#{title},#{author},#{createTime},#{views}) </insert> <select id="getBlog" resultType="blog"> select * from blog </select>
public void addBlog(){ SqlSession sqlSession = mybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); // mapper.addBlog(new Blog(IDutils.getId(), "Mybatis如此简单", "狂神说", new Date(), 999)); Blog blog = new Blog(); blog.setId(IDutils.getId()); blog.setTitle("Mybatis如此简单"); blog.setAuthor("狂神说"); blog.setCreateTime(new Date()); blog.setViews(999); mapper.addBlog(blog); blog.setId(IDutils.getId()); blog.setTitle("java如此简单"); mapper.addBlog(blog); blog.setId(IDutils.getId()); blog.setTitle("Spring如此简单"); mapper.addBlog(blog); blog.setId(IDutils.getId()); blog.setTitle("微服务如此简单"); mapper.addBlog(blog); sqlSession.commit(); List<Blog> blog2 = mapper.getBlog(); for (Blog blog1 : blog2) { System.out.println(blog1); } sqlSession.close(); }
插入完了必须要commit一下,不然数据库不显示
动态SQL——IF语句
List<Blog> getBlogIF(Map map);
<select id="getBlogIF" parameterType="map" resultType="blog"> select * from blog where 1=1 <if test="title != null"> and title = #{title} </if> </select>
动态SQL——where语句
<select id="getBlogIF" parameterType="map" resultType="blog"> select * from blog <where> <if test="title != null"> and title = #{title} </if> <if test="author != author"> and author = #{author} </if> </where> </select>
动态SQL——choose(when, otherwise)语句
<select id="getBlogIF" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title != null"> and title = #{title} </when> <otherwise> and author = #{author} </otherwise> </choose> </where> </select>
从上到下只会选择choose括号中的其中之一执行
动态SQL——set 语句
<update id="updateBlog" parameterType="map"> update blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author}, </if> </set> where id = #{id} </update>
set 元素会动态前置SET关键字,同时也会删掉无关的逗号。
动态SQL——trim(where,set)
SQL片段
有时需要将一部分功能抽取出来方便复用!
- 使用SQL标签抽取公共部分
sql id="if-title-author"> <if test="title != null"> and title = #{title} </if> <if test="author != author"> and author = #{author} </if> </sql>
2. 在需要使用的地方使用include标签引用即可
<select id="getBlogIF" parameterType="map" resultType="blog"> select * from blog <where> <include refid="if-title-author"></include> </where> </select>
注意:
Foreach
<select id="getBlogForeach" parameterType="map" resultType="blog"> select * from blog <where> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id = #{id} </foreach> </where> </select>
SqlSession sqlSession = mybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); ArrayList<Integer> ids = new ArrayList<Integer>(); ids.add(1); map.put("ids", ids); List<Blog> blogs = mapper.getBlogForeach(map); for (Blog blog : blogs) { System.out.println(blog); }
13、缓存
测试步骤:
- 开启日志
- 测试在一个session中查询两次相同记录
- 查看日志输出
一级缓存 存在于:一次session开启和关闭之间
缓存失效的情况:
- 查询不同的东西
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
- 查询不同的mapper.xml (二级缓存都不存在)
- 手动清理缓存
小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!一级缓存就是一个Map。
二级缓存:
默认情况下,只启用了本地的会话缓存,仅仅对一个会话中的数据进行缓存,要启用全局的二级缓存,只需要在SQL映射文件中添加一行
步骤:
- 开启全局缓存
<!-- 显示地开启全局缓存--> <setting name="cacheEnabled" value="true"/>
- 在要使用二级缓存的Mapper中开启
<!--在当前mapper.mxl中使用二级缓存--> <cache/>
也可以自定义参数(1min自动刷新)一般不用这些参数
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
- 测试
- 问题:需要将实体类序列化,否则会报错
小结:
查询顺序:先看二级缓存, 再查一级缓存, 都没有的话查数据库
读写分离 主从复制
Ehcache
开源java分布式缓存,主要面向通用缓存
导包:
org.mybatis.caches
mybatis-ehcache
1.2.3
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
maxElementsInMemory:缓存最大数目
相关文章:
mybatis基础学习
JDBC Mysql java基础 maven Junit 一、简介 1. 什么是mybatis MyBatis 是一款优秀的持久层框架;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原…...
【1224】数据结构(sizeof/数组的长度定义/读取字符串函数/线性表长度/左值右值/静态变量/指针与引用)
1.对一维整型数组a的正确说明是 #define SIZE 10 (换行) int a[SIZE];说法是否正确? 正确 数组的SIZE可以用宏定义,但不能用变量 2.如有定义:char str[20];,能将从键盘输入的字符串“How are you”保存到 str 数组的语句是&#x…...
解决PS 撤销卡顿
1. 关闭Windows Ink - 打开触控笔设置 - 禁用Windows Ink功能 2. 创建 PSUserConfig.txt(注意Win10/11 可能隐藏文件扩展名) - 位置:C:\Users\[用户名]\AppData\Roaming\Adobe\Adobe Photoshop CC 2019\Adobe Photoshop CC 2019 Se…...
Java 中 Stream 流的使用详解
Java 中 Stream 流的使用详解 什么是 Stream? Stream 是 Java 8 引入的一种全新的操作集合的方式。它支持通过声明性方式对集合进行复杂的数据操作(如过滤、排序、聚合等),避免使用大量的 for 循环,提高代码的可读性…...
助你通过AI培训师中级考试的目录索引
嘿,各位看官!在您正式踏入接下来的知识小宇宙之前,咱先唠唠几句… 家人们,我跟你们说,我脑一热报名了那个 AI 培训师考试。本想着开启一场知识的奇幻之旅,结果呢,学视频内容的时候,那…...
【期末复习】JavaEE(下)
1. MVC开发模式 1.1. 运行流程 1.2. SpringMVC 核心组件 1.3. 注解解释 2. ORM与MyBatis 2.1. ORM—对象关系映射 2.2. MyBatis 2.2.1. 创建步骤 会话是单例的,不能跨方法。(单例的原因主要是从数据安全角度出发) import org.apache.ibatis…...
HarmonyOS Next 实现登录注册页面(ARKTS) 并使用Springboot作为后端提供接口
1. HarmonyOS next ArkTS ArkTS围绕应用开发在 TypeScript (简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集 ArkTS在TS的基础上扩展了struct和很多的装饰器以达到描述UI和状态管理的目的 以下代码是一个基于…...
《HelloGitHub》第 105 期
兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等,涵盖多种编程语言 Python、…...
适配器模式概述
大体介绍 适配器模式(Adapter Pattern)是一种结构型设计模式,其核心目的是通过提供一个适配器类来使得原本接口不兼容的类可以一起工作。它通过将一个类的接口转换成客户端所期望的接口,使得原本因接口不兼容而无法一起工作的类可…...
跟着问题学3.1——R-CNN模型详解
R-CNN解决什么问题 前面我们介绍了经典的网络模型如AlexNet,VGG,ResNet等,这些模型要解决的任务都是分类问题,即输入一张图片,判断图片上是什么类别的物体,而且一般是单个物体。但实际中,我们会遇到一张图片上有多个或…...
微服务-1 认识微服务
目录 1 认识微服务 1.1 单体架构 1.2 微服务 1.3 SpringCloud 2 服务拆分原则 2.1 什么时候拆 2.2 怎么拆 2.3 服务调用 3. 服务注册与发现 3.1 注册中心原理 3.2 Nacos注册中心 3.3 服务注册 3.3.1 添加依赖 3.3.2 配置Nacos 3.3.3 启动服务实例 …...
25秋招面试总结
秋招从八月底开始,陆陆续续面试了不少,现在也是已经尘埃落定,在这里做一些总结一些我个人的面试经历 腾讯 腾讯是我最早面试的一家,一开始捞我面试的是数字人民币,安全方向的岗位,属于腾讯金融科技这块。…...
【C#学习——特性】
前言 C#特性学习、主要是用在数据库连接时如何动态创建对应的表,正常开发应该使用如Entity Framework等ORM框架实现自动创建生成。 代码 1、声明特性 [AttributeUsage(AttributeTargets.Property)] public class PrimaryKeyAttribute : Attribute { }[AttributeUs…...
Appscan扫出API成批分配问题解决方案
漏洞条件: 请求json参数不是接收参数的javabean及其父类中的任意属性。 意思就是:我javaben里面没有这个参数 你缺传递过来了 例如我只需要pageNum pageSize 你还传了role:admin 那么这样就有可能导致致特权升级、数据篡改、绕过安全机制 解决方案&am…...
STM32-笔记14-排队控制系统
一、项目需求 1. 红外传感器检测有人通过并计数; 2. 计数值显示在LCD1602 3. 允许通过时,LED1闪烁,蜂鸣器不响,继电器不闭合; 4. 不允许通过时,LED2闪烁,蜂鸣器响,继电器闭合&#…...
【时间之外】IT人求职和创业应知【80】-特殊日子
目录 北京冬季招聘会 OpenAI CEO炮轰马斯克 英伟达推出全新AI芯片B300 莫欢喜,总成空。本周必须要谨行慎言。 感谢所有打开这个页面的朋友。人生不如意,开越野车去撒野,会害了自己,不如提升自己。提升自己的捷径就是学习和思考…...
【GlobalMapper精品教程】090:合并多个面状图斑(以一个镇的多个村不动产宗地为例)
本文讲述在Globalmapper中,合并多个面状图斑的方法,以一个镇的多个村不动产宗地为例(假设一个镇的多个村的不动产宗地数据是分别存储在不同的村子矢量数据中,此时需要合并),点状和线状的操作方法类似。 文章目录 一、加载数据二、数据分析三、合并图斑四、注意事项一、加…...
ffmpeg之播放一个yuv视频
播放YUV视频的步骤 初始化SDL库: 目的:确保SDL库正确初始化,以便可以使用其窗口、渲染和事件处理功能。操作:调用 SDL_Init(SDL_INIT_VIDEO) 来初始化SDL的视频子系统。 创建窗口用于显示YUV视频: 目的:…...
在国产电脑上运行PDFSAM软件使用pdf分割合并交替混合处理pdf文档
软件下载地址: https://sourceforge.net/projects/pdfsam/files/ 需要注意事项,系统需要java环境,确认系统有java环境,根据软件版本需求安装对应的java运行环境。 下载pdfsam-4.3.4-linux.tar.gz安装包,解压,将runt…...
总结一下本次使用docker部署遇到的问题
1.Invalid bound statement (not found):异常 解决:原因是Dao层与动态Sql映射文件名字没有对应 2.element-plus的upload组件文件上传不成功 因为是直接请求后端不是统一的api前缀,所以nginx需要额外配置跨域 3.文件上传问题 描述:当时文…...
c#泛型学习
使用泛型的优点:使用泛型的好处包括类型安全、代码重用和性能优化。 在C#中,泛型是一种强大的工具,它允许你在编写类、接口、方法和委托时定义类型参数。这些类型参数在实例化泛型类型或调用泛型方法时被具体的类型所替代。 1. 泛型类 泛型…...
十二月第五周python
第一个程序,熟悉转换器,把加法计算器变成exe# // 1,制作加法计算器, # 输入两个数字得到相加结果并输出aint(input("输入数字:"))#int()是把输入的内容转换成整数, bint(input("输入数字:&…...
Unity中如何修改Sprite的渲染网格
首先打开SpriteEditor 选择Custom OutLine,点击Genrate 则在图片边缘会出现边缘线,调整白色小方块可以调整边缘 调整后,Sprite就会按照调整后的网格渲染了。 如何在UI中使用? 只要在UI的Image组件中选择Use Sprite Mesh 即可 结果࿱…...
修复OpenHarmony系统相机应用横屏拍照按钮点不到的问题
适配OpenHarmony系统相机应用横屏UI, 相关pr: https://gitee.com/openharmony/applications_camera/pulls/233/files 适配效果 如何安装 编译好的hap提供在附件中 1.预置在源码,随固件安装 2.安装hap hdc shell "mount -o remount,rw /"…...
keepass实现google自输入_SSH_TELNET_RDP联动
涉及到的是使用开源密码管理工具KeePass结合特定插件实现自动化密码填充的功能,特别是在谷歌浏览器中的应用。KeePass是一款强大的密码管理软件,它允许用户安全地存储各种账号的用户名和密码,并通过加密保护这些敏感信息。 1. keepass安装及配…...
电脑缺失sxs.dll文件要怎么解决?
一、文件丢失问题:以sxs.dll文件缺失为例 当你在运行某个程序时,如果系统提示“找不到sxs.dll文件”,这意味着你的系统中缺少了一个名为sxs.dll的动态链接库文件。sxs.dll文件通常与Microsoft的.NET Framework相关,是许多应用程序…...
Python实现机器学习驱动的智能医疗预测模型系统的示例代码框架
以下是一个使用Python实现机器学习驱动的智能医疗预测模型系统的示例代码框架。这个框架涵盖了数据收集(爬虫)、数据清洗和预处理、模型构建(决策树和神经网络)以及模型评估的主要步骤。 1. 数据收集(爬虫)…...
Vue3生态: 使用Vite进行高速开发
Vue3生态: 使用Vite进行高速开发 一、Vite概述 什么是Vite 法语意为 "快速")是一个为现代浏览器原生开发提供服务的构建工具。它使用ES模块作为原生浏览器加载工具,利用浏览器去解析 import 的方式加载文件,极大地加快了应用的启动…...
Android MQTT关于断开连接disconnect报错原因
最近项目遇到一个需求,就是在登录状态的时候。才能接收到消息。所有我在上线,下线状态的时候。做了MQTT断开和连接的动作。然后就是发生了。我们标题的这关键点了。直接报错了。报错的内容如下: MqttAndroidClient unregisterRecevicer afte…...
YOLO11全解析:从原理到实战,全流程体验下一代目标检测
前言 一、模型介绍 二、网络结构 1.主干网络(Backbone) 2.颈部网络(Neck) 3.头部网络(Head) 三、算法改进 1.增强的特征提取 2.优化的效率和速度 3.更高的准确性与更少的参数 4.环境适应性强 5.…...
python中函数的用法总结(二阶段)
话接上回,继续讲下函数的用法 10. 函数的注解(Function Annotations) Python 3 引入了函数注解,允许你在函数定义时给参数和返回值添加注解。注解并不影响函数的实际行为,它们更多地用于代码的可读性、文档生成以及静…...
1082 射击比赛
本题目给出的射击比赛的规则非常简单,谁打的弹洞距离靶心最近,谁就是冠军;谁差得最远,谁就是菜鸟。本题给出一系列弹洞的平面坐标(x,y),请你编写程序找出冠军和菜鸟。我们假设靶心在原点(0,0)。 输入格式:…...
模型工作流:自动化的模型内部三角面剔除
1. 关于自动减面 1.1 自动减面的重要性及现状 三维模型是游戏、三维家居设计、数字孪生、VR/AR等几乎所有三维软件的核心资产,模型的质量和性能从根本上决定了三维软件的画面效果和渲染性能。其中,模型减面工作是同时关乎质量和性能这两个要素的重要工…...
力扣题目解析--两数相除
题目 给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.…...
Python PyMupdf 去除PDF文档中Watermark标识水印
通过PDF阅读或编辑工具,可在PDF中加入Watermark标识的PDF水印,如下图: 该类水印特点 这类型的水印,会在文件的字节流中出现/Watermark、EMC等标识,那么,我们可以通过改变文件字节内容,清理掉…...
嵌入式AI STM32部署卷积神经网络的魔法棒
基于STM32部署卷积神经网络控制设备方案-AI项目-STM32部署卷积神经网络方案-红外信号复制方案-轨迹识别 项目包含下述内容 硬件部分、PCB制板、BOM表文件等等 (Hardware)外壳、3D打印文件 (3D_print)软件程序、用于电子法棒的软件程序 AI Keil等等(Software)QT上位机动作识别…...
电路元件与电路基本定理
电流、电压和电功率 电流 1 定义: 带电质点的有序运动形成电流 。 单位时间内通过导体横截面的电量定义为电流强度, 简称电流,用符号 i 表示,其数学表达式为:(i单位:安培(A&#x…...
【蓝桥杯——物联网设计与开发】系列前言
前言 本系列博客是博主为准备2024年第十五届蓝桥杯大赛物联网设计与开发赛道而写,经过4个月学习备战,最终获得全国一等奖。 从第十六届蓝桥杯大赛开始,物联网赛道更换竞赛实训平台。之前的博客,可以借鉴代码思想,但引脚…...
Linux -- 从抢票逻辑理解线程互斥
目录 抢票逻辑代码: thread.hpp thread.cc 运行结果: 为什么票会抢为负数? 概念前言 临界资源 临界区 原子性 数据不一致 为什么数据不一致? 互斥 概念 pthread_mutex_init(初始化互斥锁) p…...
免费干净!付费软件的平替款!
今天给大家介绍一个非常好用的电脑录屏软件,完全没有广告界面,非常的干净简洁。 电脑录屏 无广告的录屏软件 这个软件不需要安装,打开就能看到界面直接使用了。 软件可以全屏录制,也可以自定义尺寸进行录制。 录制的声音选择也非…...
Mybatis插件better-mybatis-generator的下载与使用
1.下载 找到设置 插件 搜索better-mybatis-generator 下载并且重启IDEA 2.连接数据库 点击测试连接 连接成功如下图 3.使用插件 选择对应的表 右击选择 注意:mysql8.0驱动一定要勾上mysql_8 其他地方不要动 然后实体类 mapper xml就都生成好了 mapper里有默认增删…...
【测试】接口测试
长期更新好文,建议关注收藏! 目录 接口规范接口测试用例设计postmanRequests 复习HTTP超文本传输协议 复习cookiesession 实现方式 1.工具 如postman ,JMeter(后者功能更全) 2.代码 pythonrequests / javahttpclient【高级】 接…...
靶机系列|VULNHUB|DC-2
免责声明: 笔记只是方便各位师傅学习知识,以下代码、网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负。 泷羽sec官网:https://longyusec.com/ 泷羽sec B站地址:https://s…...
上手教程:使用Terraform打造弹性VPC架构
最近Akamai发布的虚拟专用云(VPC)功能提供了一种隔离的网络,让云资源可以用私密的方式进行通信。 关于Akamai VPC功能,最棒的地方在于它有着极高的灵活性。用户可以通过Cloud Manager、开发人员工具(如CLI)…...
详解VHDL如何编写Testbench
1.概述 仿真测试平台文件(Testbench)是可以用来验证所设计的硬件模型正确性的 VHDL模型,它为所测试的元件提供了激励信号,可以以波形的方式显示仿真结果或把测试结果存储到文件中。这里所说的激励信号可以直接集成在测试平台文件中,也可以从…...
Kafka为什么要放弃Zookeeper
1.Kafka简介 Apache Kafka最早是由Linkedin公司开发,后来捐献给了Apack基金会。 Kafka被官方定义为分布式流式处理平台,因为具备高吞吐、可持久化、可水平扩展等特性而被广泛使用。目前Kafka具体如下功能: 消息队列,Kafka具有系统解耦、流…...
python 渗透开发工具之SQLMapApi Server不同IP服务启动方式处理 解决方案SqlMapApiServer外网不能访问的情况
目录 说在前面 什么是 SQLMapAPI 说明 sqlmapApi能干什么 sqlmapApi 服务安装相关 kali-sqlmap存放位置 正常启动sqlmap-api server SqlMapApi-Server 解决外网不能访问情况 说在前面 什么是sqlmap 这个在前面已经说过了,如果这个不知道,就可以…...
go语言的成神之路-筑基篇-gin常用功能
第一节-gin参数绑定 目录 第一节-?gin参数绑定 ShouldBind简要概述 功能: 使用场景: 可能的错误: 实例代码 效果展示 第二节-gin文件上传 选择要上传的文件 选择要上传的文件。 效果展示? 代码部分 第三节-gin请求重定向 第…...
K8S中,pod的创建流程
kubelet创建pod流程 流程图 OCI(Open Container Initiative)是一个由docker社区发起的项目,Docker、containerd CNI(Container Network Interface)网络配置:为容器分配IP地址、配置网络接口、设置路由 C…...
Windows系统提示synsoacc.dll文件报错要怎么解决?
一、文件丢失问题:深度剖析与应对策略 文件丢失是电脑运行时常见的问题之一。它可能由多种原因引起,如硬盘故障、病毒攻击、不当的文件操作等。当Windows系统提示synsoacc.dll丢失时,通常意味着该文件对于当前正在运行的程序或系统服务至关重…...