SpringBoot创建项目


SpringBoot创建项目

导入

首先把html放到template文件夹中,可以使用Thymeleaf模板功能。如果放到static文件夹下就不能用。

结构图

同样,dao,entities,controller也是放到相应的文件夹下

1568275189019

然后我是在HelloController中修改了”"和“\index”的Mapping,修改启动主页:

1
2
3
4
@RequestMapping({"/","zuye","index"})
public String ZuYe(){
return "login";
}
  • 500错误:未在pom.xml引入spring-boot-starter-thymeleaf启动器依赖

SpringBoot配置文件

首先在pom.xml中引入配置文件解释器

1
2
3
4
5
6
<!--配置文件处理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

引入了之后就会有提示了。

application文件举例

配置文件默认是application.properties或者是application.yml

举例:

application.properties

1
2
3
4
5
6
7
8
9
10
11
#配置person的值,与yml语法不一样
#引入配置文件处理器后,可以自动提示
person.last-name= 张三
person.age=19
person.birth=2001/03/04
person.boss=false
person.list=a,b,c
person.maps.k1=v1
person.maps.k2=12
person.dog.name=dog
person.dog.age=2

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server:
port: 8081

person:
lastName: zhang
age: 18
boss: false
birth: 2001/12/12

#数组、list、set
list:
- lisi
- wangwu
#数组行内写法
list: [lisi,wangwu]
#对象或Map,k-v
dog:
name: Tom
age: 3
#对象或Map行内写法
maps: {k1: v1, k2: 12}
  • yml一定要在 “冒号:” 后面加个空格,不然识别不了。

  • yml是缩进控制的语法,大小写敏感。

  • 占位符,yml和properties都支持

1
2
3
4
5
6
7
8
9
#随机uid
person.last-name=张三${random.uuid}
#随机int
person.age=${random.int}
#指定属性值
#这样出来就是“张三uid_cat”
person.cat.name=${person.name}_cat
#没有hello属性,会使用冒号后面的默认值;如果不加默认值的话,会直接输出这个式子
person.dog.name=${person.hello:hello}_dog

Profile

SpringBoot支持配置文件多Profile,形式如application-dev.yml(开发环境配置文件),application-pro.properties(生产环境配置文件)。

这样命名之后可以直接通过一些方式来指定使用哪一个配置文件。

这里的名字是可以随便起的(符合规范别xjb起就行)。

yml多文档块方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#我们假设不同环境使用的端口不同
#文档块1,prod环境,active表明现在是激活的
server:
port: 8081
spring:
profiles:
active: prod

---
#3个横线分割文档块,下面就相当于第2个文档了
#spring.profiles,就是指定是啥profiles环境了
server:
port: 8082
spring:
profiles: dev

---
#第3个也一样
server:
port: 8083
spring:
profiles: prod

指定profile

  1. 配置文件中直接写,比如上面的spring.profiles.active=prod

  2. 命令行中指定参数:
    在项目打jar包之后,使用命令行进行部署时可以像这个命令一样指定profiles:
    java -jar jar包名.jar -spring.profiles.active=dev ;
    或者可以在IDEA中运行设置,Edit Configuation中的Program arguments填写spring.profiles.active=dev

  3. 虚拟机参数:
    在Edit Configuations中的VM options::

    -Dspring.profiles.active=dev

    Edit Configurations

配置文件加载位置

  • 4个位置:

    优先级从高到低,属性不同互补,相同高的覆盖低的

    -file: /config/ 当前文件夹下的config文件夹下(比如整个项目文件夹下)
    -file: / 文件夹下根目录里
    -classpath: /config/ 类目录下的config文件夹下(IDEA里面的
    -classpath: / 类目录下的根目录里
    1567736810626

  • 指定配置文件路径

    在默认的全局配置文件中可以写:

    spring.config.loactions=G:/application.properties

配置文件加载优先级

这里具体参见官方文档

我们重点关注以下几个顺序,优先级都是从高到低的:

  1. 命令行参数

    jar包外向jar包内进行寻找

    优先加载带profile的

  2. jar包外的application-{profiles}配置文件(properties或yml)

  3. jar包内的application-{profiles}配置文件

    之后是不带profile的

  4. jar包外的application.properties或yml

  5. jar包内的application.properties或yml

  6. @Configuration注解类上的@PropertySource

自动配置的原理

这里涉及到比较底层的Spring注解,先暂时跳过了。

SpringBoot日志

日志分为日志门面(日志的抽象层)和日志实现

SpringBoot:底层是Spring框架,Spring框架默认使用JCL(很久没有更新过了)

SpringBoot选用的是SLF4(门面)和logback(实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//记录器
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void contextLoads() {
//System.out.println();

//日志的级别;
//由低到高 trace<debug<info<warn<error
//可以调整输出的日志级别;日志就只会在这个级别以以后的高级别生效
logger.trace("这是trace日志...");
logger.debug("这是debug日志...");
//SpringBoot默认给我们使用的是info级别的,没有指定级别的就用SpringBoot默认规定的级别;root级别
logger.info("这是info日志...");
logger.warn("这是warn日志...");
logger.error("这是error日志...");


}
1
2
3
4
5
6
7
8
9
  日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
-->
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n

SpringBoot修改日志的默认配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
logging.level.com.atguigu=trace


#logging.path=
# 不指定路径在当前项目下生成springboot.log日志
# 可以指定完整的路径;
#logging.file=G:/springboot.log

# 在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用 spring.log 作为默认文件
logging.path=/spring/log

# 在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n
logging.file logging.path Example Description
(none) (none) 只在控制台输出
指定文件名 (none) my.log 输出日志到my.log文件
(none) 指定目录 /var/log 输出到指定目录的 spring.log 文件中

整合JDBC

  1. 添加pom依赖
  2. 配置文件中添加数据库驱动,用户名,密码,url
  3. 新建DataSource对象getConnection()一下,来看看是否连接成功

新建项目时勾选MySQL和JDBC:

pom.xml中jdbc和mysql的依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>


<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

配置文件

然后在配置文件中,增加数据库连接的属性:

1
2
3
4
5
6
7
8
spring:
datasource:
# 数据源基本配置
initialization-mode: always
username: root
password: 123456
url: jdbc:mysql://localhost:3306/springbootlearn?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver

遇到问题:

抛出The server time zone value '�й���׼ʱ��' is unrecogni错误

原因是因为使用了Mysql Connector/J 6.x以上的版本,然后就报了时区的错误

1
2
3
4
将原来的连接:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/
修改为:
spring.datasource.url=jdbc:mysql://localhost:3306/springbootlearn?serverTimezone=UTC

添加?serverTimezone=UTC即可解决。

测试

SpringBoot测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot06DataJdbcApplicationTests {

@Autowired
DataSource dataSource;

@Test
public void contextLoads() throws SQLException {
// 输出class com.zaxxer.hikari.HikariDataSource
System.out.println(dataSource.getClass());

Connection connection = dataSource.getConnection();
// 输出HikariProxyConnection@507944445 wrapping com.mysql.cj.jdbc.ConnectionImpl@554f0dfb
// 说明SpringBoot2.0+使用的默认数据源是:com.zaxxer.hikari.HikariDataSource
System.out.println(connection);
connection.close();
}

}

使用阿里数据源Druid

  1. 添加依赖

  2. 在配置文件中增加属性,特别是type修改使用数据源

  3. 编写配置类

​ 数据源是一种提高数据库连接性能的常规手段,它会创建一个数据连接池,由它来管理数据库连接,而不需要手动的频繁打开或关闭数据库连接从而提高了性能。

​ DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,据说是目前最好的连接池。

​ 上面SpringBoot默认的数据源是com.zaxxer.hikari.HikariDataSource,然后我们将其修改为alibaba的Druid。

在上面的基础上进行修改:

pom.xml引入依赖

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>

​ 具体依赖还有各种版本可以去Maven Repository网站搜Druid

修改配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
spring:
datasource:
# 数据源基本配置
initialization-mode: always
username: root
password: 123456
url: jdbc:mysql://localhost:3306/springbootlearn?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
# schema:
# - classpath:department.sql
type: com.alibaba.druid.pool.DruidDataSource

# 数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true

# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
# 监控统计用的filter:stat;日志用的filter:log4j;防御sql注入的filter:wall
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 配置多久进行一次检测,检测需要关闭的空闲连接 单位毫秒

​ 主要是修改type,改变数据源。然后进行其他具体参数设置。

编写配置类DruidConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Configuration
public class DruidConfig {

@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid() {
return new DruidDataSource();
}

//配置Druid的监控

//1.配置一个管理后后台的servlet,这个Bean加到容器中
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");


//配置初始化参数,因为下面需要传入一个map
Map<String, String> initParams = new HashMap<>();

//这个东西的参数去StatViewServlet类里面,他的父类ResourceServlet里面找
initParams.put("loginUsername", "admin");
initParams.put("loginPassword", "123456");
// initParams.put("allow", ""); //允许谁访问,默认是所有
// initParams.put("deny","192.168.15.21"); //拒绝谁放问,这里是假设的IP地址
//需要传入一个map
bean.setInitParameters(initParams);

return bean;
}

// 2.配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();

bean.setFilter(new WebStatFilter());
Map<String, String> initParams = new HashMap<>();
//同理去看WebStatFilter里面。这个是排除的不拦截的请求
initParams.put("exclusions", "*.js,*.css,/druid/*,/druid");
bean.setInitParameters(initParams);

//要拦截哪些请求,需要一个collection参数。拦截所有请求
bean.setUrlPatterns(Arrays.asList("/*"));

return bean;
}
}

​ 总的来说,就是写了一个Servlet和一个Filter注入到容器中。

Servlet是用来处理请求,显示Druid的界面;

Filter是拦截网页的请求(部分静态资源除外),用来统计监控数据。

​ servlet是用于显示druid管理界面,并对其中的一些参数进行设置;

​ filter是对请求进行拦截,通过拦截对数据进行统计。

​ 编写完配置类之后,要在主程序入口Application上面添加@ServletComponentScan注解扫描所有的Servlet。

访问页面

访问上面设置的servlet地址即可,localhost:8080/druid,会自动跳转到登录页面,其中的账号密码就是上面设置的参数中的loginUsername和loginPassword。

Druid界面

PS:使用druid要添加log4j日志依赖,因为druid用到了这玩意,不加会报错

整合MyBatis

整合MyBatis依然在上面的基础上进行。

有两种方式:

1. 注解方式
2. xml配置文件方式

自我感觉整合MyBatis的核心就是写Mapper

何为ORM框架/Hibernate/…

先做杂项工作:编写两个POJO普通类,同时数据库中建两个相应的表。

注解版

编写Mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//指定这是一个操作数据库的mapper
@Mapper
public interface DepartmentMapper {

@Select("select * from department")
public List<Department> getDeptAll();
//这些注解都是mybatis里面规定的注解
@Select("select * from department where id=#{id}")
public Department getDeptById(Integer id);

@Delete("delete from department where id=#{id}")
public int deleteDeptByID(Integer id);

//表中的id是自增的,所以说只插入name就行了
//Options告诉Mapper我们使用了自动创建的键,键的名字是id,这时候这个注解就会自动把id在封装到返回的department里面
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into department(departmentName) values(#{departmentName})")
public int insertDept(Department department);


//根据id来更新信息
@Update("update department set departmentName=#{departmentName} where id=#{ id}")
public int updateDept(Department department);
}

关于注解中的值和参数的关系,比如#{id},对应的是下面参数中同名的参数(比如上面的Integer id);或者是下面参数是对象,对象中的同名的属性值:

使用对象

如果我们使用普通的java对象作为查询条件的参数:

1
2
@Insert("INSERT INTO T_USER(NAME, PASSWORD, PHONE) VALUES(#{name}, #{password}, #{phone})")
int insertByUser(User user);

只需要语句中的#{name}、#{age}就分别对应了User对象中的name和age属性。

1
2
3
4
5
User u = new User();
u.setName("赵六");
u.setPassword("12312312");
u.setPhone("13888888888");
userMapper.insertByUser(u);
@Insert("INSERT INTO T_USER(NAME, PASSWORD, PHONE) VALUES(#{name}, #{password}, #{phone})")
int insertByUser(User user);
只需要语句中的#{name}、#{age}就分别对应了User对象中的name和age属性。
User u = new User();
    u.setName("赵六");
    u.setPassword("12312312");
    u.setPhone("13888888888");
    userMapper.insertByUser(u);

关于这里的传参方式还有几种,比如@Param注解、使用Map、还有上面的使用对象。具体参见这个CSDN博客

编写Mapper:

  1. 使用@Mapper注解来说明这是个Mybatis的Mapper映射,然后写成接口?
  2. 自定义方法,名字随意,方法参数和注解相对应(如上传参方式)
  3. 注解@Insert,@Select等等,在这种注解中写相应的SQL语句(参数如上)。

编写Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@RestController
public class DeptController {

@Autowired
DepartmentMapper departmentMapper;

@GetMapping("/dept/all")
public List<Department> getDeptAll(){
List<Department> list = departmentMapper.getDeptAll();
//System.out.println(list.toString());
return list;
}
//在参数中加上id传过来,然后去查询这个id
//localhost:8080/dept/select/1
@GetMapping("/dept/select/{id}")
public Department getDepartment(@PathVariable("id") Integer id) {
return departmentMapper.getDeptById(id);
}

//插入department,带参数?departmentName = xxx
//比如:localhost:8080/dept/insert?departmentName=腾讯
@GetMapping("/dept/insert")
public Department insertDept(Department department) {
departmentMapper.insertDept(department);
return department;
}

//删除
@GetMapping("/dept/delete/{id}")
public Department deleteDeptbyId(@PathVariable("id") Integer id) {
Department deleteDept = getDepartment(id);
departmentMapper.deleteDeptByID(id);
return deleteDept;
}
}

编写Controller来进行测试,调用Mapper中的方法,进行增删改查。注意这里用URL传参的方式。

XML配置文件版

编写Mapper接口

这里的Mapper类不需要写注解,只是包含相应的自定义方法,还有方法的参数。其他的相关东西都在xml里。

1
2
3
4
5
6
7
8
//需要用@Mapper或者@MapperScan将接口扫描装配到容器中
public interface EmployeeMapper {

public Employee getEmpById(Integer id);

public void insertEmp(Employee employee);
}

编写mybatis-config.xml

在里面进行各种配置。

1
2
3
4
5
6
7
8
9
10
<?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>
<!--驼峰命名法,解决d_id和dId的问题-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>

​ 这里主要是解决了d_id和dId不同的问题,如果不加这个配置,那么在数据库中的d_id和类中的dId不会匹配。

编写Mapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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 namespace="com.song.springboot.mapper.EmployeeMapper">
<!-- public Employee getEmpById(Integer id);

public void insertEmp(Employee employee);-->
<!--这两个方法,用标签的形式在这绑定-->
<!--id是方法名,resultType是返回值-->
<select id="getEmpById" resultType="com.song.springboot.bean.Employee">
SELECT * FROM employee WHERE id=#{id}
</select>
<!--插入,不需要返回值;然后注意这个d_id->dId这个大小写转换-->
<insert id="insertEmp">
INSERT INTO employee(lastName,email,gender,d_id) VALUES (#{lastName},#{email},#{gender},#{dId})
</insert>
</mapper>

注意的地方:

  • namespace:Mapper类名具体的路径
  • id:应该是和方法名相对应
  • resultType:返回值类型,也是具体的类路径;返回值为空就不用写
  • 在相应的比如<select></select>编写SQL语句即可,值和参数仍然和上面的一样。

修改application.yml配置文件

1
2
3
4
#mybatis配置xml方式,xml所在路径
mybatis:
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml

在配置文件中加如上属性,指定mybatis的xml配置文件所在路径。

整合JPA

整合JPA时也可将数据源换为Druid,但是这里就没再换。

何为JPA

JPA是Java持久化API,定义了一系列对象持久化的标准,而hibernate是当前非常流行的对象持久化开源框架,Spring boot就默认集成了这种框架,加速web应用开发。

spring-boot-starter-data-jpa的启动器中继承了Hibernate

这里我们用的Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,是基于Hibernate之上构建的JPA使用解决方案。

JPA == Java Persistence API(Java 持久层 API):用于对象持久化的 API

作用:使得应用程序以统一的方式访问持久层

JPA 是 Hibernate 的一个抽象,就像 JDBC 和 JDBC 驱动的关系。JPA是一种规范,而Hibernate是JPA的一个实现。

参考:

JPA与ORM

img

整合JPA

pom.xml依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- Spring Data的 Jpa starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

PS:spring-boot-starter-data-jpa包括了Hibernate。

application.yml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
datasource:
url: jdbc:mysql://localhost:3306/jpa?serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
# 自动更新或者创建数据表
ddl-auto: update
# 控制台显示SQL
show-sql: true
# bean的声明可以被覆盖?应该是用于我们自定义的Repository接口注入
main:
allow-bean-definition-overriding: true

前面DataSource的配置和以前一样,url用户名密码驱动等。

下面的overriding如果不写的话启动不了,根据控制台建议写上的。

实体类

重点在实体类的注解上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//使用JPA注解配置映射关系
@Entity //告诉JPA这是个实体类
@Table(name = "tbl_user") //告诉这个类和数据库中的哪个表相对应,不写的话就是默认的小写类名user
@JsonIgnoreProperties(value={"hibernateLazyInitializer","handler"})
public class User {
@Id //说明这是主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //自增主键
private Integer id;

@Column(name = "last_name",length = 50) //指定列名
private String lastName;

@Column //默认就是列名和属性名一样,emali
private String email;

//Getter and Setter
public Integer getId() {
return id;
}

...

}
  • @Entiy:说明这是一个POJO类
  • @Table:指明这个类和数据库中哪张表映射
  • @Id:表示这个属性在表中是主键
  • @GeneratedVlue:说明了主键的生成方式
  • @Column:指定这个属性和表中哪个字段对应

继承JpaRepository接口

1
2
3
4
//JpaRepository后面的泛型:要操作的实体类,以及主键的类型
public interface UserRepository extends JpaRepository<User, Integer> {

}

Controller 调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@RestController
public class UserController {

@Autowired
UserRepository userRepository;


//查找全部
@GetMapping("/user/jpa/all")
public List<User> getAllUser(){
return userRepository.findAll();
}
//查找用户
@GetMapping("/user/jpa/select/{id}")
public User getUser(@PathVariable("id") Integer id) {
User user = userRepository.getOne(id);
return user;
}

//插入用户,参数方式
@GetMapping("/user")
public User insertUser(User user) {
User saveUser = userRepository.save(user);
return saveUser;
}
}

这里调用的话就直接自动注入一个Repository,然后用它来调用相应的方法如findAll()、getOne()等等即可(Hibernate会自动生成数据库语句,不用手动写)。

在请求的同时,因为在application.yml中添加了show-sql: true属性,所以说console会打印出SQL语句:

控制台SQL语句

SpringBoot 注解

/hello请求

  • @SpringBootApplication
    标注一个主程序类,表明这是一个SpringBoot应用,程序开始入口

    1
    2
    3
    4
    5
    6
    7
    8
    @SpringBootApplication
    public class SpringBoot01HelloworldQuickApplication {

    public static void main(String[] args) {
    SpringApplication.run(SpringBoot01HelloworldQuickApplication.class, args);
    }

    }
  • @ResponseBody
    表明这是一个响应
    表示该方法的返回结果直接写入HTTP response body中
    一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@responsebody后,会直接返回json数据。

  • @RequestMapping(“/hello”)
    响应/hello请求

  • @Controller

    是个Spring、SpringMVC的注解,@Controller 控制层,@Service 服务层,@Repository 持久层或者这里

  • @RestController:
    用于标注控制层组件(如struts中的action),包含**@Controller和@ResponseBody**。

1
2
3
4
5
6
7
8
9
10
11
/*@ResponseBody
* @Controller
* 这两个注解可以合并成下面一个*/
@RestController
public class HelloController {

@RequestMapping("/hello")
public String hello() {
return "Hello Quick Spring Boot!!!";
}
}

配置文件,使用properties或yml注入Bean

  • @Component
    泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
    @Component和@Bean都是用来注册Bean并装配到Spring容器中,但是Bean比Component的自定义性更强。可以实现一些Component实现不了的自定义加载类。

  • @PropertySource(value = {“classpath:person.properties”})
    从指定的路径中导入配置文件(不指定这个注解的话,会自动从默认的全局配置文件application.properties中导入)

  • @ConfigurationProperties(prefix = “person”)
    将配置文件(比如application.properties)中的person属性的值与当前类的属性绑定

  • @Value(“${person.last-name}”)
    将这个值绑定到标注下面的属性上

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @Component
    @PropertySource(value = {"classpath:person.properties"})
    @ConfigurationProperties(prefix = "person")
    public class Person {
    //Value赋值,一个一个的
    // $ 取资源文件中的值
    //@Value("${person.last-name}")
    private String lastName;
    // # 计算表达式
    //@Value("#{11*2}")
    private Integer age;
    //直接赋值
    //@Value("true")
    private Boolean boss;
    private Date birth;

    private Map<String,Object> maps;
    private List<Object> list;
    private Dog dog;

    ......

    }

  • @ConfigurationProperties和@Value的比较
  1. 一般@Value导入时,是一个值一个值的导入,而@ConfigurationProperties是一批导入。
  2. @Value支持SPEL,而@ConfigurationProperties比如
  • @Value(${person.last-name}):绑定person.last-name到当前属性
  • @Value(#{11*2}):计算11*2,就是22
  • @Value(“true”):直接赋值为 true
  1. @Value不支持松散语法。

    • 在松散语法中,last-name与lastName是一样的,-n就是大写的N,而不支持松散语法的话就不一样
  2. @Value不支持JSR303数据校验

    • 就是说下面的这个@Email,会校验绑定的这个属性是否符合格式
    • 加上@Email之前,需要一个这个@Validated注解
    • @Email:校验数据是否邮箱格式
    • 如果校验不通过的话会抛IllegalStateException异常,数据不能绑定
    • @Validated的详细使用
  3. @Value不支持复杂类型封装


配置类

  • @ImportResource(locations = {“classpath:beans.xml”})

    标注在一个配置类上

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @ImportResource(locations = "classpath:bean.xml")
    @SpringBootApplication
    public class SpringBoot01HelloworldQuickApplication {

    public static void main(String[] args) {
    SpringApplication.run(SpringBoot01HelloworldQuickApplication.class, args);
    }

    }

    导入spring的配置文件,bean.xml

    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="helloService" class="com.song.springboot.service.HelloService"></bean>
    </beans>

    这个xml的作用就是给HelloService类中注入一个id为helloService的Bean,可以在test中用以下来验证:

    1
    2
    3
    4
    5
    6
    7
    8
    @Autowired
    ApplicationContext ioc;

    @Test
    public void testHelloService(){
    boolean b = ioc.containsBean("helloService");
    System.out.println(b);
    }
  • @Autowired

    自动注入。可以对成员变量、方法和构造函数进行标注。@Autowired注解的意思就是,当Spring发现@Autowired注解时,将自动在代码上下文中找到与其匹配(默认是类型匹配)的Bean,并自动注入到相应的地方去。

  • @Confgiuration

    指出该类是 Bean 配置的信息源,相当于XML中的,一般加在主类上。说明这个是个配置类。

  • @Bean

    相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //说明这是个配置类,返回一个HelloService对象注入到容器中,最后ioc容器中会含有一个helloService
    @Configuration
    public class MyAppConfig {
    @Bean
    public HelloService helloService(){
    return new HelloService();
    }

    }

遇到的一些问题解决

添加schema-all.sql到resources中,不会自动建表

错误:

​ 添加schema-all.sql到resources中,不会自动建表

解决:

​ 在application.properties配置文件中添加:

1
2
##   加这个,不然不会执行schema-all或者下面自定义sql脚本
spring.datasource.initialization-mode=always

同时,如果加schema自定义sql位置,也是需要加是这个always属性的

1
2
##   设置默认的sql位置
spring.datasource.schema=classpath:department.sql

修改Durid,新建config配置绑定属性:

报错:

1
Failed to bind properties under 'spring.datasource' to javax.sql.DataSource

原因:

在yml配置文件中,设置的属性:

1
2
3
4
5
6
##   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

其中filters引用了log4j,但是pom.xml中没有,所以说报错

解决:

在pom.xml中添加log4j的依赖:

1
2
3
4
5
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>

SpingBoot+Druid,404错误

错误:

网页404:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sat Sep 28 09:21:10 CST 2019

There was an unexpected error (type=Not Found, status=404).

No message available

原因:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  //1.配置一个管理后后台的servlet,这个Bean加到容器中
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/");


//配置初始化参数,因为下面需要传入一个map
Map<String, String> initParams = new HashMap<>();

//这个东西的参数去StatViewServlet类里面,他的父类ResourceServlet里面找
initParams.put("loginUsername", "admin");
initParams.put("loginPassword", "123456");
// initParams.put("allow", ""); //允许谁访问,默认是所有
// initParams.put("deny","192.168.15.21"); //拒绝谁放问,这里是假设的IP地址
//需要传入一个map
bean.setInitParameters(initParams);

return bean;
}

其中的ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/");,第二个参数,urlMappings写错了。应该是"/druid/*",是druid 路径下所有映射。

修改:

1
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

即可。

整合JPA,报错Caused by: org.hibernate.AnnotationException: No identifier specified for entity:

错误:

这个前面还有个错误,好像是什么不能注入容器,根据console提示修改一下yml就行啦,加上:

1
2
3
spring: 
main:
allow-bean-definition-overriding: true

大概是在容器中把我们写的UserRepository注入进去覆盖原来自有的?

启动SpringBoot时报错:

1
2
3
4
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: com.song.springboot.entity.User


Caused by: org.hibernate.AnnotationException: No identifier specified for entity: com.song.springboot.entity.User

原因:

百度了半天,都在说是实体类的注解写错了,需要加上主键的@Id,我对了半天也没发现错误。最终发现:

1
2
3
import org.springframework.data.annotation.Id;

import javax.persistence.*;

还真是@Id的问题,因为我加@Id的时候导包导入错了,不是springframework的@Id注解而是persistence里面的。

解决:

删掉上面那个id的import就行。

JPA查询报错

错误:

Controller中查找方法:

1
2
3
4
5
6
//查找用户
@GetMapping("/user/jpa/select/{id}")
public User getUser(@PathVariable("id") Integer id) {
User user = userRepository.getOne(id);
return user;
}

访问这个url然后报错:

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.song.springboot.entity.User$HibernateProxy$j7VI3Nk2["hibernateLazyInitializer"])

原因:

​ 因为返回值是个实体类对象的原因。

​ hibernate会给每个被管理的对象加上hibernateLazyInitializer属性,同时struts-jsonplugin或者其他的jsonplugin都是如此。

​ 因为jsonplugin用的是java的内审机制,hibernate会给被管理的pojo加入一个hibernateLazyInitializer属性。jsonplugin通过java的反射机制将pojo转换成json,会把hibernateLazyInitializer也拿出来操作。但是hibernateLazyInitializer无法由反射得到,也就是说从对象中无法获取,所以就抛异常了。

解决:

在User实体类中加入如下注解:

1
2
3
4
5
6
7
//使用JPA注解配置映射关系
@Entity //告诉JPA这是个实体类
@Table(name = "tbl_user") //告诉这个类和数据库中的哪个表相对应,不写的话就是默认的小写类名user
@JsonIgnoreProperties(value={"hibernateLazyInitializer","handler"})
public class User {
...
}

在类上声明的@JsonIgnoreProperties,是忽略Hibernate的延迟加载的一些属性”hibernateLazyInitializer”, “handler”, “fieldHandler”,这些属性在实体类里没有所以要忽略掉,否则会报错。

Java反射来输出对象的所有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public static void testReflect(Object model) throws Exception {
Field[] field = model.getClass().getDeclaredFields(); //获取实体类的所有属性,返回Field数组
for (int j = 0; j < field.length; j++) { //遍历所有属性
String name = field[j].getName(); //获取属性的名字

System.out.println("attribute name:" + name);
name = name.substring(0, 1).toUpperCase() + name.substring(1); //将属性的首字符大写,方便构造get,set方法
String type = field[j].getGenericType().toString(); //获取属性的类型
if (type.equals("class java.lang.String")) { //如果type是类类型,则前面包含"class ",后面跟类名
Method m = model.getClass().getMethod("get" + name);
String value = (String) m.invoke(model); //调用getter方法获取属性值
if (value != null) {

System.out.println("attribute value:" + value);
}
}
if (type.equals("class java.lang.Integer")) {
Method m = model.getClass().getMethod("get" + name);
Integer value = (Integer) m.invoke(model);
if (value != null) {
System.out.println("attribute value:" + value);
}
}
if (type.equals("class java.lang.Short")) {
Method m = model.getClass().getMethod("get" + name);
Short value = (Short) m.invoke(model);
if (value != null) {
System.out.println("attribute value:" + value);
}
}
if (type.equals("class java.lang.Double")) {
Method m = model.getClass().getMethod("get" + name);
Double value = (Double) m.invoke(model);
if (value != null) {
System.out.println("attribute value:" + value);
}
}
if (type.equals("class java.lang.Boolean")) {
Method m = model.getClass().getMethod("get" + name);
Boolean value = (Boolean) m.invoke(model);
if (value != null) {
System.out.println("attribute value:" + value);
}
}
if (type.equals("class java.util.Date")) {
Method m = model.getClass().getMethod("get" + name);
Date value = (Date) m.invoke(model);
if (value != null) {
System.out.println("attribute value:" + value.toLocaleString());
}
}
}
}

使用sql将一个表的数据导入另一个表:

1
insert into jc_rw(guid,rwid,xmbh,xmmc,xfsj) (select rwid,rwid,xmbh,xmmc,xfsj from jc_db_task);

设置一个表中的某一列全部为这个数据:

1
update jc_rw set cdrwks='监测科';

整合Mybatis-Plus和Lombok

主要是mybaits-plus

  1. 修改pom文件,添加依赖

  2. 修改yml配置文件,将mybatis的配置删除,增加mybatis-plus配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    ## MyBatis配置
    ## https://baomidou.com/config/
    mybatis-plus:
    ## 对应的 XML 文件位置
    mapperLocations: classpath*:mapper/**/*Mapper.xml
    ## 实体扫描,多个package用逗号或者分号分隔
    typeAliasesPackage: com.ruoyi.**.domain
    ## 针对 typeAliasesPackage,如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
    #typeAliasesSuperType: Class<?>
    ## 如果配置了该属性,SqlSessionFactoryBean 会把该包下面的类注册为对应的 TypeHandler
    #typeHandlersPackage: null
    ## 如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
    #typeEnumsPackage: null
    ## 启动时是否检查 MyBatis XML 文件的存在,默认不检查
    checkConfigLocation: false
    ## 通过该属性可指定 MyBatis 的执行器,MyBatis 的执行器总共有三种:
    ## SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句(PreparedStatement)
    ## REUSE:该执行器类型会复用预处理语句(PreparedStatement)
    ## BATCH:该执行器类型会批量执行所有的更新语句
    executorType: SIMPLE
    ## 指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
    configurationProperties: null
    configuration:
    ## 自动驼峰命名规则(camel case)映射
    ## 如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
    mapUnderscoreToCamelCase: true
    ## 默认枚举处理类,如果配置了该属性,枚举将统一使用指定处理器进行处理
    ## org.apache.ibatis.type.EnumTypeHandler : 存储枚举的名称
    ## org.apache.ibatis.type.EnumOrdinalTypeHandler : 存储枚举的索引
    ## com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler : 枚举类需要实现IEnum接口或字段标记@EnumValue注解.
    defaultEnumTypeHandler: org.apache.ibatis.type.EnumTypeHandler
    ## 当设置为 true 的时候,懒加载的对象可能被任何懒属性全部加载,否则,每个属性都按需加载。需要和 lazyLoadingEnabled 一起使用。
    aggressiveLazyLoading: true
    ## MyBatis 自动映射策略
    ## NONE:不启用自动映射
    ## PARTIAL:只对非嵌套的 resultMap 进行自动映射
    ## FULL:对所有的 resultMap 都进行自动映射
    autoMappingBehavior: PARTIAL
    ## MyBatis 自动映射时未知列或未知属性处理策
    ## NONE:不做任何处理 (默认值)
    ## WARNING:以日志的形式打印相关警告信息
    ## FAILING:当作映射失败处理,并抛出异常和详细信息
    autoMappingUnknownColumnBehavior: NONE
    ## Mybatis一级缓存,默认为 SESSION
    ## SESSION session级别缓存,同一个session相同查询语句不会再次查询数据库
    ## STATEMENT 关闭一级缓存
    localCacheScope: SESSION
    ## 开启Mybatis二级缓存,默认为 true
    cacheEnabled: true
    global-config:
    ## 是否打印 Logo banner
    banner: true
    ## 是否初始化 SqlRunner
    enableSqlRunner: false
    dbConfig:
    ## 主键类型
    ## AUTO 数据库ID自增
    ## NONE 空
    ## INPUT 用户输入ID
    ## ASSIGN_ID 全局唯一ID
    ## ASSIGN_UUID 全局唯一ID UUID
    idType: AUTO
    ## 表名前缀
    tablePrefix: null
    ## 字段 format,例: %s,(对主键无效)
    columnFormat: null
    ## 表名是否使用驼峰转下划线命名,只对表名生效
    tableUnderline: true
    ## 大写命名,对表名和字段名均生效
    capitalMode: false
    ## 全局的entity的逻辑删除字段属性名
    logicDeleteField: null
    ## 逻辑已删除值
    logicDeleteValue: 1
    ## 逻辑未删除值
    logicNotDeleteValue: 0
    ## 字段验证策略之 insert,在 insert 的时候的字段验证策略
    ## IGNORED 忽略判断
    ## NOT_NULL 非NULL判断
    ## NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
    ## DEFAULT 默认的,一般只用于注解里
    ## NEVER 不加入 SQL
    insertStrategy: NOT_NULL
    ## 字段验证策略之 update,在 update 的时候的字段验证策略
    updateStrategy: NOT_NULL
    ## 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
    selectStrategy: NOT_NULL


文章作者: SongX64
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 SongX64 !
  目录