MyBatis作用
MyBatis 是一个强大的 SQL 映射框架,它主要用于在 Java 应用程序中简化数据库交互。MyBatis 的核心功能是将 Java 对象(实体类)映射到数据库表,并且能够将 SQL 查询的结果映射回 Java 对象。这样的机制不仅提高了开发效率,还有助于维护清晰的应用程序结构。
MyBatis 的主要特点包括:
-
SQL 映射: MyBatis 允许开发者直接编写原生 SQL 语句,并通过 XML 或注解的方式配置这些 SQL 语句。这提供了极高的灵活性,可以实现几乎所有 SQL 操作。
-
数据绑定: MyBatis 支持复杂的数据绑定操作。例如,可以将数据库中的行自动映射成为 Java 对象,也可以将 Java 对象中的字段自动绑定到 SQL 语句中的参数。
-
会话管理: MyBatis 管理数据库连接和事务的生命周期,使开发者可以专注于业务逻辑而非底层数据库操作。
-
插件和拦截器: MyBatis 允许开发者通过插件修改其核心行为,如查询处理、结果集处理等,增加了扩展性。
Mybatis操作
MyBatis配置文件
<!----> <!--MyBatis 依赖:--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> <!-- 更早的版本,与 JDK 11 兼容 --> </dependency> <!--MyBatis 测试依赖:--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter-test</artifactId> <version>2.1.4</version> <!-- 更新测试依赖的版本 --> <scope>test</scope> </dependency> <!--MySQL 运行时依赖:--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- Lombok 依赖:--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
实体类Pojo层
Lombok是一个使用的java类库,能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化java开发、提高效率
注解 | 作用 |
---|---|
@Getter/@Setter | 为所有属性提供get/set方法 |
@ToString | 自动生成toSting |
@EqualsAndHashCode | 生成equals和hashcode方法 |
@Date | @Getter+@Setter+@ToString+@EqualsAndHashCode |
@NoArgsConstructor | 生成无参构造器 |
@AllArgsConstructor | 生成有参构造器(除了static修饰的字段以外) |
@Data @AllArgsConstructor @NoArgsConstructor public class emp { private Integer id; private String username; private String password; }
properties属性文件
#驱动类名称 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #数据库连接url spring.datasource.url=jdbc:mysql://localhost:3306/db03 #连接数据库的用户名 spring.datasource.username=root #连接数据库的密码 spring.datasource.password=1234 #配置mybatis的日志, 指定输出到控制台 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl #开启mybatis的驼峰命名自动映射开关 a_column ------> aCloumn mybatis.configuration.map-underscore-to-camel-case=true
如何修改properties属性文件的规范
-
打开 IntelliJ IDEA: 启动您的 IntelliJ IDEA 编辑器。
-
进入设置: 在菜单栏中选择
File
->Settings
(如果您使用的是 macOS,则是IntelliJ IDEA
->Preferences
)。 -
编辑器设置: 在设置窗口中,导航到
Editor
->File Encodings
。 -
设置全局编码: 在
Global Encoding
下拉菜单中选择UTF-8
,以设置全局默认编码。 -
设置项目编码: 在
Project Encoding
下拉菜单中也选择UTF-8
,确保整个项目文件的编码统一。 -
设置 Properties 文件编码: 向下滚动到
Default encoding for properties files
,并在这里选择UTF-8
。这确保了所有.properties
文件都将以 UTF-8 编码打开和保存。 -
保存设置: 点击
OK
或Apply
保存设置。
Mapper数据访问层
@Mapper public interface empMapper { @Delete("delete from emp where id= #{id}") public int deleteid(Integer id); }
数据库连接池
原理类似于线程池。先创建一些连接。连接自动分配
mybatis基础操作
mybatis占位符
在Mybatis中提供的参数占位符有两种:${...} 、#{...}
#{} 执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值 使用时机:参数传递,都使用#{…} delete from emp where id= #{id} -- 输入16 先预编译 delete from emp where id= ? ${...} delete from emp where id= #{id} 输入16 直接编译为delete from emp where id= 16 相当于直接把参数拼接到SQL语句,在运行 拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题 使用时机:如果对表名、列表进行动态设置时使用
日志输出
在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果。具体操作如下:
-
打开application.properties文件
-
开启mybatis的日志,并指定输出到控制台
#指定mybatis输出日志的位置, 输出控制台 mybatis.configuration.logimpl=org.apache.ibatis.logging.stdout.StdOutImpl
==> Preparing: delete from emp where id= ? ==> Parameters: 15(Integer) <== Updates: 1
但是我们发现输出的SQL语句:delete from emp where id = ?,我们输入的参数15并没有在后面拼接,id的值是使用?进行占位。那这种SQL语句我们称为预编译SQL。
预编译SQL
预编译SQL有两个优势:
-
性能更高
-
更安全(防止SQL注入)
性能更高:预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句 时,不会再次编译。(只是输入的参数不同) 更安全(防止SQL注入):将敏感字进行转义,保障SQL的安全性。
SQL注入
SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些 SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
sql注入攻击
-- 用户名随便,密码' or '1'='1 select count(*) from emp where username='asdad'and password='' or '1'='1';
如果使用预编译,会把代码预编译,在插入内容
select count(*) from emp where username=? and password=?;
删除
public interface empMapper { @Delete("delete from emp where id= #{id}") public int deleteid(Integer id);//删除成功返回1,失败0 } @Delete注解:用于编写delete操作的SQL语句
插入
@Insert("insert into emp(username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time) "+ "values ((#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})") public void insert(emp emp);
返回主键
//会自动将生成的主键值,赋值给emp对象的id属性 @Options(useGeneratedKeys = true,keyProperty = "id") @Insert("insert into emp(username, name, gender, image, job,entrydate, dept_id, create_time, update_time) "+"values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})") public void insert(Emp emp);
更新修改
@Update("update emp set username=#{username}, name=#{name},gender=#{gender}, image=#{image}"+ ", job=#{job}, entrydate=#{entrydate}, dept_id=#{deptId}, update_time=#{updateTime} where id=#{id}") public void update(emp emp);
查询
数据封装
起别名查询
@Select("select id, username, password, name, gender, image, job,entrydate, " + //给带_的起一个和emp实体类属性一样的别名 "dept_id AS deptId, create_time AS createTime, update_time ASupdateTime " + "from emp " +"where id=#{id}") public Emp getById(Integer id);
手动映射(繁琐)
@Results({ @Result(column = "dept_id",property = "deptId") , @Result(column = "create_time",property = "createTime") , @Result(column ="update_time",property = "updateTime") }) @Select("select * from emp where id=#{id}") public emp getbyid(Integer id);
开启mybatis开启驼峰的设置(建议)
properties属性文件配置
#开启mybatis的驼峰命名自动映射开关 mybatis.configuration.map-underscore-to-camel-case=true
代码例子
@Select("select * from emp where id=#{id}") public emp getbyid(Integer id);
条件查询
concat拼接
concat('%',#{name},'%')
// @Select("select * from emp where name like '%张%' and gender=1 and entrydate between '2010-01-01' and '2020-01-01'" + // " order by update_time desc ;") // public List<emp> list(); //为什么不使用'%#{name}%',因为#{name}最后要预编译为?,如果?出现在了''就会被当作字符串。 //如果必须使用#可以通过concat进行拼接 @Select("select * from emp where name like concat('%',#{name},'%') and gender=#{gender} and entrydate between #{begin} and #{end}" + " order by update_time desc ;") // @Select("select * from emp where name like '%${name}%' and gender=#{gender} and entrydate between #{begin} and #{end}" + // " order by update_time desc ;") public List<emp> list(String name, short gender, LocalDate begin,LocalDate end);
@Test public void selectid(){ List<emp> list=empmapper.list("张", (short) 1, LocalDate.of(2010,1,1),LocalDate.of(2020,1,1)); list.stream().forEach(System.out::println); }
XML映射文件
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。
-
XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。
-
XML映射文件的namespace属性为Mapper接口全限定类名一致。
-
XML映射文件中sql语句的id与Mapper 接口中的方法名一致,并保持返回类型一致。
Mapper接口
@Mapper public interface empMapper { public List<emp> list(String name, short gender, LocalDate begin,LocalDate end); }
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"> <!--namespace=Mapper接口的全类名--> <mapper namespace="com.example.Mapper.empMapper"> <!--id=方法名--> <!--resultType(翻译为:结果的类型) 但是resultType指的是输出内容单条记录所封装的类型。 虽然下面代码输出的是一个集合,但是单条记录所封装的是emp,所以 resultType="com.example.Pojo.emp"--> <select id="list" resultType="com.example.Pojo.emp"> select * from emp where name like concat('%',#{name},'%') and gender=#{gender} and entrydate between #{begin} and #{end} order by update_time desc </select> </mapper>
为什么需要有三条规范
是为了让方法能找到sql语句。
MyBatis动态SQL
动态SQL:
随着用户输入或外部条件的变化而变化的SQL语句称为SQL语句,称为动态SQL
<select id="list" resultType="com.example.Pojo.emp"> select * from emp where namelike concat('%',#{name},'%') and gender=#{gender} and entrydate between #{begin} and #{end} order by update_time desc </select> 这个SQL必须几个条件同时输入才能查询,如果想要用一个条件查询就不行了
动态SQL-if
<if test="..">..</if>:用于判断条件是否成立,使用test属性进行条件判断,如果为true,则拼接SQL语句 <where> </where> <set>用于删除字段,原理同<where>类似
<select id="list" resultType="com.example.Pojo.emp"> select * from emp <!--如果使用where会出错 错误1.如果第一条不成立,第二条成立会导致SQL语句为select * from emp where and gender=? 错误2.如果都不成立,会导致SQL语句出现select * from emp where order by update_time desc --> <where> <!-- 1.自动去除条件前面的and或or等连接符 2.根据子标签动态生成where关键字,如果里面条件都不成立不会生成where关键字--> <if test="name != null"> name like concut('%',#{name},'%') </if> <if test="gender != null"> and gender=#{gender} </if> <if test="begin != null and end != null"> and entrydate between #{begin} and #{end} </if> </where> order by update_time desc </select>
动态SQL-foreach
foreach遍历
<!--批量删除员工(15,16,17)--> <delete id="deleteid"> delete from emp where id in <!-- collection:要遍历的集合 item:遍历出来的元素 separator:遍历出来的元素使用什么进行分隔 open:遍历开始前拼接的SQL片段 close:遍历结束后拼接的SQL片段 --> <!--(15,16,17)--> <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> <!--运行结果==> Preparing: delete from emp where id in ( ? , ? , ? ) Parameters: 15(Integer), 16(Integer), 17(Integer)--> </delete>
动态SQL-sql和include
xml有多个sql的重复代码,可以把他们放到一个标签,使用的时候在抽取
存放
<sql id="commonSelect"> select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp </sql>
引用
<select id="list" resultType="com.example.Pojo.emp"> <!--抽取代码 refid=需要引用的标签的id名 --> <include refid="commonSelect"/> <where> <if test="name != null"> name like concut('%',#{name},'%') </if> <if test="gender != null"> and gender=#{gender} </if> <if test="begin != null and end != null"> and entrydate between #{begin} and #{end} </if> </where> order by update_time desc </select>