目录
一、介绍
这是三个框架。
Spring就是一个Ioc容器,帮助我们管理对象。
SpringBoot框架是为了简化Spring,集成了很多框架,能够让我们快速的开发出一个Spring程序。
SpringMVC又叫Spring Web MVC,是用于开发Web应用和网络接口的,所以它是一个Web框架。
二、学习Spring
Java程序的一个原则:高内聚低耦合。
高内聚好比是一个班级内的同学们联系紧密,团结互助,一个模块内的程序紧密联系。
低耦合好比是一个年级内的各个班级,这个年纪要去比赛,但是有一个班级出问题了,时时不能参加,导致整个年级不能去参加,耽误整个年级的荣誉,也就是程序中的每个模块联系不能紧密,要分工干活。
为了能够实现高内聚低耦合,就要使用Spring框架。
1、两大核心
Spring的两大核心是IoC和DI。
IoC的意思是控制权反转,原来逻辑是:谁用对象,谁就要去创建对象,现在权力反转了,不再需要自己去创建了,Spring会帮助创建好对象,只要有谁需要用到,就会给谁。这个操作实现了高内聚低耦合,不再因为仅仅要改一个小地方的代码,导致一连串的代码都需要改的这种问题。
DI的意思是依赖注入,依赖的意思是对象,谁需要对象,就要把对象注入给谁。
2、存和取对象
那么它既然是一个管理对象的容器,就肯定要有存对象和取对象的功能。
存对象:使用五大类注解和@Bean。
首先需要明白注解是干什么用的?我们要知道程序是为了解决生活中的问题的,那我们现在用的spring框架,它是可以管理我们程序的,那么使用注解,就是程序中的某一块代码在告诉spring,你去干什么干什么,怎么操作我。
取对象:三种注入方式。
三、学习Mybaties
1、介绍
Mybaties是一个持久层框架,是用来操作数据库的。
2、操作数据库的步骤
操作数据库要分为以下三步:
- 1、导入Mybatis依赖和MySQL驱动依赖。
<!--Mybatis 依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
- 2、在application.yml中配置数据库相关参数。
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
- 3、在数据库中创建需要的表,并在idea中创建出对应的实体类,并加上@Data注解,表中的字段要与类中的属性名一 一对应。
- 4、创建持久层接口:XxxInfoMapper,并加上@Mapper注解,在类中写出操作数据库的相关代码。
- 5、最后可以使用idea创建测试类,加上@SpringBootTest注解,去加载Spring的运行环境,加上@Slf4j注解,可以在控制台打印出日志,最后再去测试写出的代码是否正确。
3、具体操作数据库的方法
两种:第一种:通过注解开发数据库;第二种:通过XML方式开发。
下面通过增删改查的操作去具体介绍两种方法。
第一种(注解):
查:
在XxxInfoMapper的类中,编写selectAll接口,使用@Select注解
@Select("select * from userinfo") //方法的实现
List<UserInfo> selectAll(); //方法的声明
增:
在XxxInfoMapper的类中,编写insert接口,使用@insert注解实现,看insert方法的参数,传递的一个对象,那么sql语句中,怎么接收到对象中的属性值呢?直接写出对象中的属性变量名即可,这也就要求了,必须要和实体类中的属性名要写的一模一样。
//增
//insert方法的实现
@Insert(" insert into userinfo (username,password,age,gender,phone ) " +
" values (#{username},#{password},#{age},#{gender},#{phone}) ")
//方法的声明
Integer insert(UserInfo userInfo);
删:
//方法实现
@Delete("delete from userinfo where id = #{id} ")
//方法定义
Integer delete(Integer id);
改:
@Update("update userinfo set age=#{age} where id=#{id}")
Integer update(UserInfo userInfo);
要注意的问题:
1:传参问题
selectOne方法传了一个id值,如果只传一个值,就像现在这样,sql语句中接收id值的变量可以随意起名,但是一般要与方法中的形参保持一致。
@Select("select * from userinfo where id = #{id}")
UserInfo selectOne( Integer id);
//也可以对id重命名,使用@Param重命名
//UserInfo selectOne(@Param("useid") Integer id);
2:重命名问题
若此时对 对象重命名,注解中还能自动获取到对象中的值吗?答案是不能!,要采用对象名.属性名的方法获取。如下图所示:
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert(" insert into userinfo (username,password,age,gender,phone ) " +
" values (#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender},#{userInfo.phone}) ")
Integer insert(@Param("userInfo")UserInfo userInfo);
3:获取自增ID问题
对于增 这一操作,如何获取自增ID呢,因为在实际操作中,后续可能会用到ID值去做一些事情。
如下图所示,使用@Options注解,两个参数,第一个参数的意思是:是否要获得并使用key值,第二个参数用来接收key值,此时赋值给了实体类中的id。
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert(" insert into userinfo (username,password,age,gender,phone ) " +
" values (#{username},#{password},#{age},#{gender},#{phone}) ")
Integer insert(UserInfo userInfo);
4:映射问题
对于查这一操作,是如何获取到数据库中的数据的呢? 通过Mybaties将数据库中的字段与实体类中的属性名进行映射,获得数据库中的想要的数据。这就要求字段名与属性名要一 一对应,但是若字段名与属性名不匹配怎么办?有以下三种解决办法:
- 对字段起别名,起与属性名相同的名字
@Select("select username,password,age,gender,phone,delete_flag as deleteflag,create_time as createtime,update_time as updatetime from userinfo")
List<UserInfo> selectAll();
- 利用映射规则,通过@Results注解告诉Mybaties如何进行映射
如下图所示:
@Results(value = {
@Result(column = "delete_flag",property = "deleteflag"),
@Result(column = "create_time",property = "createtime"),
@Result(column = "update_time",property = "updatetime")
})
@Select("select * from userinfo where id = #{useid}")
UserInfo select1(@Param("useid") Integer id);
如果其他的select方法也需要这种操作,要重写一遍这个注解吗?心中要有一个字:“懒”,只要给Results起个名即可。
@Results(id = "Map", value = {
@Result(column = "delete_flag",property = "deleteflag"),
@Result(column = "create_time",property = "createtime"),
@Result(column = "update_time",property = "updatetime")
})
@Select("select * from userinfo where id = #{useid}")
UserInfo select1(@Param("useid") Integer id);
//在其他select方法处,使用@ResultMap方法,可以达成复用。
@ResultMap(value = "Map")
@Select("select * from userinfo where id = #{useid}")
UserInfo select2(@Param("useid") Integer id);
- 开启驼峰命名
潜规则:字段名若有多个单词命名,用下横线间隔,而实体类中的属性名用小驼峰法命名。
那我现在不想手动操作了,Mybaties可以帮我将字段名自动转成小驼峰命名的名字吗?答案是可以!使用配置的方式。(在application.yml中)
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰⾃动转换
第二种(XML):
通过XML方式开发,与注解不同的是,不使用注解去实现操作数据库的方法,而是在xml文件中编写方法的实现。
步骤:
- 编写xml文件
这是xml文件的声明,声明此文件是在实现哪个接口,要写出接口的全限定类名。(在XML中声明的)
声明的下面这个接口:
- 指明xml文件所在的地址(在yml文件中写)
mybatis:
configuration:
# map-underscore-to-camel-case: true #配置驼峰自动转换
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印Sql语句
mapper-locations: classpath:mapper/**Mapper.xml #指明xml文件所在地址
#配置的名字要与实现的xml文件的名字对应上
查:
id的名字要与要实现的方法的名字一模一样。
查询到的数据要返回给select方法,所以resultMap要等于要返回的数据的权限的类名。看下面第二个图,返回的数据的类型是UserInfo类。
<select id="selectAll" resultType="com.bite.demo.model.UserInfo">
select * from userinfo
</select>
增: (自增ID的获取方法与注解相同)
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username,password,age,gender,phone)
values(#{username},#{password},#{age},#{gender},#{phone})
</insert>
删:
<delete id="delete">
delete from userinfo where id=#{id}
</delete>
改:
<update id="update">
update userinfo set gender = #{gender}
where id = #{id}
</update>
要注意的问题:
1:插的重命名问题
Integer insert2(@Param("userInfo") UserInfo userInfo);
解决方法也是与注解类型:(通过对象名.属性名去获得)
<insert id="insert2" useGeneratedKeys="true" keyProperty="id">
insert into userinfo (username,password,age,gender,phone)
values(#{userInfo.username},#{userInfo.password},
#{userInfo.age},#{userInfo.gender},#{userInfo.phone})
</insert>
2:查中的映射问题
由于数据库的字段名与实体类中的有的属性名不一致,映射不上,导致有的字段赋值不了属性,导致查出来的数据部分为默认值。
方法:(与注解相似,但是没有通过注解的方法(@Results))
- 给字段起别名(与注解相同)
- 通过在配置文件写上相应配置代码(与注解相同)
mybatis:
configuration:
# map-underscore-to-camel-case: true #配置驼峰自动转换
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印Sql语句
mapper-locations: classpath:mapper/**Mapper.xml
- 结果映射:使用ResultMap,先起个别名,随便起。type表示类型,想要通过数据库的字段映射成什么类型,就去写什么类型,不过一定要是全限定类名。
<resultMap id="XmlBaseMap" type="com.bite.demo.model.UserInfo">
<id column="id" property="id"></id>
<result column="delete_flag" property="deleteFlag"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
</resultMap>
接着再写:
//这是原来的
<select id="selectAll" resultType="com.bite.demo.model.UserInfo">
select * from userinfo
</select>
/*这是有了ResultMap之后。不用再写resultType了,
直接用上面写的resultMap,既成功映射了,又表示了要返回的数据的类型。*/
<select id="selectAll2" resultMap="XmlBaseMap">
//resultMap的名字要与上面写的resultMap的名字一样
select * from userinfo
</select>
4、 #和$
1、 #和$分别代表的SQL是什么?
- 使用#去赋值,输入参数给SQL语句后,不会去拼接到SQL中,而是用?这个符号去占位。
接口中:
测试类中(把1这个数字传给selectOne方法):
控制台:
所以#这种不用拼接的SQL叫做预编译SQL。
- 换成$去赋值,再去编译:
测试类:
控制台:
此时1,直接拼接到SQL语句中了,并没有占位 ,这种SQL是即时SQL。
2、#和$的区别
他俩的区别就是预编译SQL与既是SQL的区别
首先先要了解SQL的执行过程:
第一步:语法编译,第二步:SQL优化,第三步:SQL执行。
预编译SQL在一次编译后会缓存这一条SQL语句,再一次编译时,不会执行前两步,直接执行第三步,也就是提前挖好坑一个坑,就等着参数传进去执行了,所以会提高效率。
而即时SQL每次都会执行这三步,不会挖坑。
所以区别就是:
预编译SQL性能高,且不存在SQL注入的问题。
SQL注入:给sql传进去的参数,改变了SQL的正常执行流程,导致出现错误。
例如:
使用这条SQL语句会从左到右拼接 ,导致会查出所有。
为什么即时SQL会出现SQL注入的问题?
因为会进行SQL拼接,导致SQL语句被改变。
3、#和$的用法
当使用#赋值时,若参数类型为String时,会自动给参数加上单引号,但有的时候,不需要加单引号,所以要用$赋值。
例如:进行排序操作,不能使用#;当表名,字段名作为参数时,也不能使用#。
这是一个排序操作,此时使用#的话,会给sort加上单引号,但是不用给它加,加了会报错的,#区分不了什么时候加,什么时候不加,太死心眼,遇到String类型的,就会无脑加。
在接口中:
有个例外:模糊查询,使用#会报错,使用$会造成SQL注入的问题,只能用内置函数concat()函数搭配#使用。
模糊查询:使用#
报错:
使用$ :
没有报错
5、数据库连接池
项目要运行,要进行数据库的连接,过后,还要释放连接,但现在提前储存连接,需要的时候直接拿就可以,存放那些连接的地方就叫做连接池,这样做提高了效率。