为什么Service和Dao层放在一起写,是因为Service业务与Dao层紧密关联,当然在测试阶段,如果在没有Dao层的情况下,我们可以在Service里放一些伪数据来方便测试!
首先,肯定是需要安装数据库了,本教程中使用的是Mysql数据库,安装步骤就不再说了,默认大家都已安装好了Mysql并可以连接使用!
注意:本教程中连接的是远程服务器,mysql,redis等都是放在远程服务器中的,服务器系统为Linux,因此建议看此系列教程的同学最好能有一样的服务器环境,会减少出错几率,后面可能会讲解docker在linux上的使用方法,如果环境不一样,会浪费不少时间!
此篇文章,我们先来使用JPA,来作为数据持久化层,需要引入的资源有jdbc、jpa、mysql-connector、druid,druid是什么?
阿里官方定义:
Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能
我们使用来Druid代替原生的数据源,正如它所说,它提供了强大的监控能力,所有有关数据库的查询信息,都可以在Druid后台查看!
pom.xml引入相关资源:
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!--持久化框架jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--mysql数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--druid数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.1</version>
</dependency>
application.yml中配置:
spring:
resources:
static-locations: classpath:/static/, classpath:/templates/
datasource:
username: root
password: root
url: jdbc:mysql://39.xxx.x.xx:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#druid
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: stat,wall,slf4j,config
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
jpa: #JPA配置
hibernate:
ddl-auto: none #数据库有表就更新,无表就创建
show-sql: true #显示sql语句
数据库datasource的用户名,密码,数据库名,地址,这些安装时都应该知道,替换上即可!其它的可以原封不动的复制下来,因为这些配置几乎都是固定的!
创建DruidConfig类,来配置druid相关的一些东西:
DruidConfig:
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid() {
return new DruidDataSource();
}
/**
* 配置druid监控路径、登录账号密码,以及其它一些参数
* (访问链接:http://localhost:8080/byl/druid)
*
* @return
*/
@Bean
public ServletRegistrationBean<StatViewServlet> statViewServlet() {
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
Map<String, String> initParas = new HashMap<>();
initParas.put("loginUsername", "admin");//druid登录用户名
initParas.put("loginPassword", "admin");//druid登录密码
initParas.put("allow", "localhost");//只允许本机访问,多个ip用逗号隔开
//initParas.put("deny","");//拒绝访问
initParas.put("resetEnable", "false");//禁用HTML页面的Reset按钮
bean.setInitParameters(initParas);
return bean;
}
/**
* 设置过滤器
*
* @return
*/
@Bean
public FilterRegistrationBean<WebStatFilter> webStatFilter() {
FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new WebStatFilter());
bean.addUrlPatterns("/*");
Map<String, String> initParams = new HashMap<>();
initParams.put("exclusions", "*.js,*.css,/druid/*");//排除不需要监控的资源
bean.setInitParameters(initParams);
return bean;
}
}
注意 @Configuration注解,SpringBoot中所有配置相关的类都会通过它注解生效!而 @Bean则是向SpringBoot容器中添加组件,并交由SpringBoot管理!
在数据库中创建表(测试过程中可以随意创建,仅测试dao与数据库的连通性即可):
创建对应数据库表的Bean:
User:
package com.byl.springboottest.bean;
import javax.persistence.*;
@Entity(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)//自增主键
private Integer id;
@Column
private Integer dept_id;
@Column
private String user_name;
@Column
private String nick_name;
@Column
private String password;
public Integer getDept_id() {
return dept_id;
}
public void setDept_id(Integer dept_id) {
this.dept_id = dept_id;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getNick_name() {
return nick_name;
}
public void setNick_name(String nick_name) {
this.nick_name = nick_name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
@Entity(name = "user")声明对应的表名,@Column声明对应的字段名,需要注意的是,如果数据库中定义的字段带下划线“_”,那么我们的Bean的映射字段要采用驼峰命名规则,因此需要修改为:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)//自增主键
private Integer id;
@Column(name="dept_id")
private Integer deptId;
@Column(name="user_name")
private String userName;
@Column(name="nick_name")
private String nickName;
@Column
private String password;
书写Dao(JPA创建Dao方式,继承JpaRepository):
UserRepository:
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
不要忘了@Repository注解!其中JpaRepository<User, Integer>的第一个参数就是对应的Bean,第二个参数则是该Bean中(数据库中)对应的Id的类型!
此时打开test文件夹下的SpringboottestApplicationTests类,引入资源UserRepository ,并添加测试方法:
@SpringBootTest
class SpringboottestApplicationTests {
@Resource
UserRepository userRepository;
@Test
void contextLoads() {
}
@Test
void testDao() {
Optional<User> optional = userRepository.findById(2);
System.out.println("获取用户成功>>" + optional.get().getNickName());
}
}
执行testDao方法:
运行结果:
此时表明,数据库连接,映射,操作都已经没有问题了!
浏览器打开:http://localhost:8080/byl/druid
输入DruidConfig中配置的用户名(admin)和密码(admin)进入:
刚才我们的查询操作记录,已经静静的躺在那里了!
继续往下说,关于JPA的用法,JPA本身提供了一些基础的增删改查方法:
不用写sql而直接调用方法使用,我们也可以在UserRepository定义复杂的查询方法,比如JPA原生只提供了findById的方法,我想通过用户名查怎么办?
那就可以自己写一个findByUserName的方法:
你会发现,它提供了一些列关于字段的操作方法,完全不在需要你去手写sql了,当然如果遇到比较复杂需要手写sql的时候,jpa也是支持的:
@Query(value = "select * from user where id = ?", nativeQuery=true)
User getUser(Integer id);
更详细的用法,可以看文档喽!
测试通过后,我们终于来到了业务层Service,在写Service之前,我们要了解Service层的结构,就是接口+实现的方式:
首先定义interface:
public interface UserService {
User getUserById(Integer id);
User getUserByName(String username);
}
然后写实现类Impl:
public class UserServiceImpl implements UserService{
@Override
public User getUserById(Integer id) {
return null;
}
@Override
public User getUserByName(String username) {
return null;
}
}
为什么一定要先写个接口,再写个实现类呢,为什么要这么麻烦,我直接写一个Service,咔咔的就开始写业务岂不是更简单吗?
如果有这个疑问,建议可以先了解一下interface 这个东西,Java为啥要出这么个东西?!
这里我只简单的说下它的两个优点:
1.制定规范:我定义一个了interface,却可以有n个实现,我的控制层并不关心你的实现方法,我只调用我定义的interface,你的实现只用给我返回我需要的数据就可以了!这样的好处就是,一旦业务有所变动,我只需要修改实现方法即可,甚至可以有多套实现来回切换!SpringBoot中也大量运用这种思想,定义接口,第三方实现,那么我在项目中就可以随意去切换第三方的实现库了!
2.适合团队开发:当在开发一个新项目时,可能要分工了,A写Controller层,B写Service层,A在写Controller层时肯定需要调用Service的东西,但是此时Service层并没有东西呢怎么调?难道我要等待B把Service都写好了我再开始干活?或者我先定义一堆假数据自己先玩着,等B把Service写好后在去修改?这样都极大了拖慢了工作效率,那么此时A或B就可以去定义Service接口了,这将很快,之后A就可以直接去调用接口了,而不用在等待B的具体业务实现!
我们继续完成UserServiceImpl 的实现:
@Service
public class UserServiceImpl implements UserService {
@Resource
UserRepository userRepository;
@Override
public User getUserById(Integer id) {
return userRepository.findById(id).get();
}
@Override
public User getUserByName(String username) {
return userRepository.findByUserName(username);
}
}
注意不要忘了添加 @Service注解,代表它是一个Service!
UserController:
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
UserService userService;
@GetMapping("/{id}")
public User getUserById(@PathVariable Integer id) {
return userService.getUserById(id);
}
@GetMapping("/getUserByName")
public User getUserByName(String username) {
return userService.getUserByName(username);
}
}
浏览器输入:http://localhost:8080/byl/user/2
输入:http://localhost:8080/byl/user/getUserByName?username=baiyuliang
这样我们就完成了一个简单的从Controller到Service再到Dao层的接口请求!