一、Spring
- 简化开发
- IOC
- AOP
- 框架整合
- Mybatis
- Mybatis-plus
- …
怎么学:学习spring框架的设计思想;学习基础操作;学习案例
1.核心概念
1.IOC:控制反转
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象的创建控制权由程序转移到外部
- Spring提供一个容器,称为Ioc容器,用来充当Ioc思想中的”外部“
- IOC容器否则对对象的创建、初始化等一系列工作,被创建或被管理的对象在Ioc容器中统称为Bean
2.DI:依赖注入
- 在容器中建立bean与bean之间的依赖关系的整个过程
- Spring使用Java Bean对象的Set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程
3.AOP:面向切面编程
在不惊动原始设计的基础上为其进行功能增强
IOC入门案例:
-
导入spring的坐标spring-context
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency>
-
定义spring管理的类
public interface BookService { public void save(); } public class BookServiceImpl implements BookService { private BookDao bookDao = new BookDaoImpl(); @Override public void save() { bookDao.save(); System.out.println("book service save..."); } }
-
创建spring配置文件,配置bean
<?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"> <!--id属性给bean起名字,class属性表示给bean定义类型--> <bean id="bookDao" class="com.xin.dao.impl.BookDaoImpl"></bean> <bean id="bookService" class="com.xin.service.impl.BookServiceImpl"></bean> </beans>
-
初始化Ioc容器,通过容器获取bean
public static void main(String[] args) { //获取IOC容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); BookService bookService = (BookService) ctx.getBean("bookService"); bookService.save(); } }
DI入门案例:
-
删除使用new的形式创建对象的代码
public class BookServiceImpl implements BookService { private BookDao bookDao; @Override public void save() { bookDao.save(); System.out.println("book service save..."); } }
-
提供依赖对象对应的setter方法
public class BookServiceImpl implements BookService { private BookDao bookDao; @Override public void save() { bookDao.save(); System.out.println("book service save..."); } public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
-
配置service和dao之间的关系
<?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"> <!--id属性给bean起名字,class属性表示给bean定义类型--> <bean id="bookDao" class="com.xin.dao.impl.BookDaoImpl"></bean> <bean id="bookService" class="com.xin.service.impl.BookServiceImpl"> <!--配置service与dao的关系--> <!--property标签表示配置当前bean的属性 name属性表示配置哪一个具体的属性 ref属性表示参照哪一个bean --> <property name="bookDao" ref="bookDao"></property> </bean> </beans>
2.bean
1.bean的基本配置
起别名
在写Spring配置文件的时候,也可以使用name属性来给bean起多个名称(别名),这样就可以使用配置的其他名称来使用bean,需要注意的是,要使用的别名必须在spring配置文件中被定义,否则会抛出一个异常,起多个别名的时候可以使用逗号(,)封号(;)空格( )隔开
作用范围
spring默认提供的bean对象是单例的,也就是说如果使用getbean获取同一个bean,获取两次并直接将该对象打印的时候,会发现两个对象的地址值是相同的,但是开发环境中有时候需要创建非单例的对象,这个时候就需要给bean配置一个作用范围了(scope)
- scope作用范围默认只有两个值
- prototype 非单例
- singleton 单例,不配置scope属性默认的是这个值
- 适合交给容器进行管理的bean:表现层对象、业务层对象、数据层对象、工具对象
- 不适合交给容器进行管理的bean:封装实体的域对象
2.bean的实例化
bean本质上就是对象
-
构造方法:提供可访问的构造方法(无参构造方法)
-
通过静态工厂创建对象
//静态工厂 public class BookDaoFactory { public static BookDao getBookDao(){ return new BookDaoImpl(); } } //配置 <bean id="bookDao1" factory-method="getBookDao" class="com.xin.factory.BookDaoFactory"></bean>
-
实例工厂创建
//实例工厂 public class BookDaoFactory { public BookDao getBookDao(){ return new BookDaoImpl(); } } //配置:先将工厂bean配置出来 <bean id="bookFactory" class="com.xin.factory.BookDaoFactory"></bean> <bean id="bookDao2" factory-method="getBookDao" factory-bean="bookFactory"></bean>
-
FactoryBean 代替原始实例工厂中创建对象的方法,默认是单例的
public class BookDaoFactoryBean implements FactoryBean<BookDao> { @Override public BookDao getObject() throws Exception { return new BookDaoImpl(); } @Override public Class<?> getObjectType() { return BookDao.class; } @Override public boolean isSingleton() { return false; } } <bean id="bookDao3" class="com.xin.factory.BookDaoFactoryBean"></bean>
3.bean生命周期控制
bean的生命周期:bean从创建到销毁的整个过程
bean的生命周期控制:在bean创建后到销毁前做一些事情
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save...");
}
public void init(){
System.out.println("init...");
}
public void destroy(){
System.out.println("destroy...");
}
}
<bean id="bookDao" class="com.xin.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"></bean>
Ⅰ.初始化容器
- 创建对象(内存分配)
- 执行构造方法
- 执行属性注入( set操作)
- 执行bean初始化方法
Ⅱ.使用bean
- 执行业务操作
Ⅲ.关闭/销毁容器
- 执行bean销毁方法
3.依赖注入
1.依赖注入方式
-
setter注入
//在bean中定义引用类型属性并提供可访问的set方法 public class BookDaoImpl implements BookDao { private int number; public void setNumber(int number) { this.number = number; } } //配置中使用property标签value属性注入简单类型数据 <bean id="bookDao" class="com.xin.dao.impl.BookDaoImpl"> <property name="number" value="100"></property> </bean>
-
构造器注入
<bean id="bookDao" class="com.xin.dao.impl.BookDaoImpl"></bean> <bean id="bookService" class="com.xin.service.impl.BookServiceImpl"> <!--name为构造器形参变量名--> <constructor-arg name="bookDao" ref="bookDao"></constructor-arg> </bean>
2.依赖自动装配
Ioc容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称bean,因变量名与配置耦合,不推荐使用
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
3.集合注入
<bean id="programmer" class="com.model.Programmer">
<property name="cars">
<!-- 1. list数据注入 //有序集合-->
<list>
<value>ofo</value>
<value>mobai</value>
<value>宝马</value>
</list>
</property>
<property name="pats">
<!-- 2. set数据注入 //无序集合-->
<set>
<value>小黑</value>
<value>小红</value>
<value>小白</value>
</set>
</property>
<property name="infos">
<!-- 3. map数据注入 -->
<map>
<entry key="name" value="cjx"></entry>
<entry key="age" value="23"></entry>
<entry key="id" value="20821111355"></entry>
</map>
</property>
<property name="mysqlInfos">
<!-- 4. properties数据注入 //实际也是set类型是无序的-->
<props>
<prop key="url">mysql:jdbc://localhost:3306/dbname</prop>
<prop key="user">root</prop>
<prop key="password">123456</prop>
</props>
</property>
<property name="numbers">
<!-- 5. 数组的数据注入 -->
<array>
<value>哥哥</value>
<value>弟弟</value>
<value>妹妹</value>
<value>姐姐</value>
</array>
</property>
</bean>
spring加载properties文件:
-
开启context命名空间
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
-
使用context命名空间加载properties文件
<context:property-placeholder location="classpath:*.properties"/>
-
使用属性占位符${}读取properties文件中的属性
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
4.注解开发模式
注解开发定义bean:
-
使用@Component定义bean
@Component("userDao") public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("user save..."); } }
@Component("userDao") 等价于在核心配置文件中 <bean id="bookDao" class="com.xin.dao.impl.BookDaoImpl"></bean>
-
核心配置文件中通过组件扫描加载bean
<!--扫描基于注解的方式配置bean--> <context:component-scan base-package="com.xin.dao.impl"/>
spring提供@Component注解的三个衍生注解,方便理解:
- @Controller:用于表现层bean定义
- @Service:用于业务层bean定义
- @Repository:用于数据层bean定义
纯注解开发:
-
用配置类代替xml配置
@Configuration @ComponentScan("com.xin") public class SpringConfig {}
-
使用配置类加载spring容器
public class AppForAnnotation { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); UserDao userDao = (UserDao) ctx.getBean("userDao"); userDao.save(); } }
1.bean作用范围和生命周期
@Component("userDao")
@Scope("prototype")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("user save...");
}
@PostConstruct
public void init() {
System.out.println("init...");
}
@PreDestroy
public void destroy() {
System.out.println("destroy...");
}
}
2.依赖注入
-
使用@Autowired注解开启自动装配模式,使用@Qualifier注解开启指定名称装配bean
@Service public class UserServiceImpl implements UserService { @Autowired //多个userDaoImpl,即多个相同类型的bean @Qualifier("userDao") private UserDao userDao; @Override public void save() { System.out.println("user service save..."); userDao.save(); } }
3.管理第三方bean
-
将独立的配置类加入核心配置
public class JdbcConfig { // 1.定义一个方法获得要管理的对象 // 2.添加@Bean,表示当前方法的返回值是一个bean @Bean public DataSource dataSource() { DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jc.jdbc.Driver"); ds.setUrl(""); ds.setUsername(""); ds.setPassword(""); return ds; } } @Configuration @Import(JdbcConfig.class) public class SpringConfig { }
-
第三方bean依赖注入
- 引用类型:方法形参
- 简单类型:成员变量
4.spring整合mybatis
-
引入坐标
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency>`
-
设置配置类
@Configuration @ComponentScan("com.xin") @PropertySource("jdbc.properties") @Import({JdbcConfig.class, MybatisConfig.class}) public class SpringConfig {}
public class MybatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean sfb = new SqlSessionFactoryBean(); sfb.setTypeAliasesPackage("com.xin.pojo"); sfb.setDataSource(dataSource); //设置驼峰命名 Configuration configuration = new Configuration(); configuration.setMapUnderscoreToCamelCase(true); sfb.setConfiguration(configuration); return sfb; } @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer configurer = new MapperScannerConfigurer(); configurer.setBasePackage("com.xin.dao"); return configurer; } }
5.spring整合junit
//引入坐标
<!--整合junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
//编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testSelectAll() {
System.out.println(userService.selectAll());
}
}
5.AOP
面向切面编程,在不惊动原始设计的基础上为其进行功能增强
1.核心概念
-
连接点 ( JoinPoint)∶程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等,在SpringAOP中理解为方法的执行
-
切入点(Pointcut ) :匹配连接点的式子
- 在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
- 一个具体方法:com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
- 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
- 在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
-
通知( Advice ) :在切入点处执行的操作,也就是共性功能
- 在SpringAOP中,功能最终以方法的形式呈现
-
通知类:定义通知的类
-
切面(Aspect ):描述通知与切入点的对应关系
2.入门案例
-
导入aop相关坐标
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency>
-
定义接口和实现类
public interface BookDao { public void save(); public void update(); } @Repository public class BookDaoImpl implements BookDao { @Override public void save() { System.out.println(System.currentTimeMillis()); System.out.println("book dao save..."); } @Override public void update() { System.out.println("book dao update..."); } }
-
定义通知类,切入点,绑定切入点和通知关系
@Component @Aspect public class MyAdvice { //定义切入点 @Pointcut("execution(void com.xin.dao.BookDao.update())") private void pt() { } //绑定切入点和功能的关系 @Before("pt()") public void method() { System.out.println(System.currentTimeMillis()); } }
-
开启spring对aop注解驱动支持
@Configuration @ComponentScan("com.xin") @EnableAspectJAutoProxy public class SpringConfig { }
3.工作流程
- Spring容器启动
- 读取所有切面配置中的切入点
- 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
- 匹配失败,创建对象
- 匹配成功,创建原始对象(目标对象)的代理对象
- 获取bean执行方法:获取bean,调用方法并执行,完成操作
4.切入点表达式
标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
@Pointcut("execution(void com.xin.dao.BookDao.update())")
使用通配符描述切入点:
- :单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution (public * com.itheima..UserService.find*(*))
匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法 - …:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution (public User com…UserService.findById (…) )匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法 - +:专用于匹配子类类型
execution(* * …Service+.(…))
建议:
- 所有代码按照标准规范开发,否则以下技巧全部失效
- 描述切入点通常描述接口,而不描述实现类
- 访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)
- 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
- 包名书写尽量不使用…匹配,效率过低,常用*做单个包描述匹配,或精准匹配
- 接口名/类名书写名称与模块相关的采用匹配,例如UserService书写成Service,绑定业务层接口名
5.通知类型
-
前置通知@Before:在切入点方法调用之前执行的通知。
-
后置通知@After:在切入点方法调用之后执行的通知。(无法与异常通知同时执行)
-
环绕通知@Around:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行
@Around("pt()") public Object around(ProceedingJoinPoint point) throws Throwable { System.out.println("around before advice..."); Object proceed = point.proceed(); System.out.println("around after advice..."); return proceed; }
-
异常通知@AfterThrowing:在程序发生异常时执行的通知。(无法与后置通知同时执行)
-
最终通知@AfterReturning:切入点方法调用之后执行的通知,与后置通知不同的是,无论切入点方法是否正常执行都会执行该通知。
6.应用场景
场景一: 记录日志
场景二: 监控方法运行时间 (监控性能)
场景三: 权限控制
场景四: 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
场景五: 事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )
6.spring事务
事务的作用:在数据层保障一系列的数据库操作同成功同失败
spring事务的作用:在数据层或业务层保障一系列的数据库操作同成功同失败
二、SpringMVC
1.springmvc简介
- 一种基于Java实现的MVC模型的轻量级web框架
- 优点:使用简单,开发便捷
快速入门:
-
导入坐标
<!--1.导入坐标springmvc与servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.15</version> </dependency>
-
创建springmvc控制器类
@Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save() { System.out.println("user save..."); return "{'module':'springmvc'}"; } }
-
初始化springmvc环境,设定springmvc对应的bean
//3.创建springmvc的配置文件,加载controller对应的bean @Configuration @ComponentScan("com.xin.controller") public class SpringMvcConfig { }
-
初始化servlet容器,加载springmvc环境,并设置springmvc技术处理的请求
//4,定义一个servlet容器启动的配置类,在里面加载spring的配置 public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //加载springmvc容器配置 @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } //设置哪些请求归属springmvc处理 @Override protected String[] getServletMappings() { return new String[]{"/"}; } //加载spring容器配置 @Override protected WebApplicationContext createRootApplicationContext() { return null; } }
2.请求和响应
@RequestBody与@RequestParam区别
- 区别
- @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
- @RequestBody用于接收json数据【application/json)
- 应用
- 后期开发中,发送json格式数据为主,@RequestBody应用较广
- 如果发送非json格式数据,选用@RequestParam接收请求参数
3.REST风格
REST表示访问资源的格式,隐藏了资源访问行为
使用行为动作区分对资源进行的操作
- GET (常用于查询)
- POST(常用于新增/保存)
- PUT(常用于更新/修改)
- DELETE(常用于删除)
根据REST风格对资源进行访问叫做RESTful
@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user) {
System.out.println("user save..." + user);
return "{'module':'user save'}";
}
@RequestMapping(value = "/users", method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user) {
System.out.println("user update..." + user);
return "{'module':'user update'}";
}
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id) {
System.out.println("user delete..." + id);
return "{'module':'user delete'}";
}
4.SSM整合
SSM整合流程
-
创建工程
-
SSM整合
- spring
- SpringConfig
- MyBatis
- MyBatisConfig
- JdbcConfig
- jdbc.properties
- SpringMVC
- ServletConfig
- SpringMvcConfig
- spring
-
功能模块
- 表与实体类
- dao(接口+自动代理)
- service(接口+实现类)
- 业务层接口测试(整合JUnit )
- controller
- 表现层接口测试( PostMan )
设置同一数据返回结果类:
public class Result {
private Integer code;
private Object data;
private String msg;
}
设置统一数据返回结果编码:
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
}
异常处理器:便于快速统一地处理异常
@RestControllerAdvice
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class)
public Result doException(Exception ex) {
return new Result(500, null, "出错了");
}
}
自定义异常:程序出现异常抛出自定义异常
public class SystemException extends RuntimeException {
private Integer code;
public SystemException(Integer code) {
this.code = code;
}
public SystemException(Integer code, String message) {
super(message);
this.code = code;
}
public SystemException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
5.拦截器
一种动态拦截方法调用地机制,在springmvc中动态拦截控制器方法的执行
作用:
- 在指定的方法调用前后执行预先设定的代码
- 阻止原始方法的执行
拦截器和过滤器的区别
- 归属不同:Filter属于Servlet技术,Interceptor属于SprintMVC技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对springmvc的访问进行增强
@Component
public class ProjectInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
三、Maven
分模块开发
将原生模块按照功能拆分成若干个子模块,方便模块间的相互调用,接口共享
步骤:
- 创建maven模块
- 书写模块代码
- 通过maven指令安装模块到本地仓库(install)
依赖传递:
- 直接依赖 :在当前项目中通过依赖配置建立的依赖关系
- 间接依赖 :资源如果依赖其他资源,当前项目间接依赖其他资源
依赖冲突:
- 路径优先 :当依赖中出现相同的资源时,层级越深,优先级越低;层级越浅,优先级越高
- 声明优先 :当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的
- 特殊优先 :当同级配置了相同资源的不同版本,后配置的覆盖先配置的
依赖管理:
- 可选依赖:指对外隐藏当前所依赖的资源——不透明
- 排除依赖:指主动断开依赖的资源,被排除的资源无需指定版本——不需要
聚合和继承
maven-parent:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xin</groupId>
<artifactId>maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!--1.父工程的打包方式为pom-->
<packaging>pom</packaging>
<modules>
<module>../maven-dao</module>
</modules>
<!--2.在父工程的pom文件中配置依赖关系,子工程将沿用父工程中的依赖关系-->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
</dependencies>
<!--3.配置子工程中可选的依赖关系-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
maven-dao:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xin</groupId>
<artifactId>maven-dao</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<!--4.在子工程中配置当前工程所继承的父工程-->
<parent>
<groupId>com.xin</groupId>
<artifactId>maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!--快速找到对应的parent-->
<relativePath>../maven-parent/pom.xml</relativePath>
</parent>
<dependencies>
<!--5.在子工程中配置使用父工程中可选依赖的坐标-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
多环境开发
<!--配置多环境-->
<profiles>
<!--开发环境-->
<profile>
<id>env_dep</id>
<properties>
<jdbc.url>jdbc:mysql://127.0.0.1:3306/db</jdbc.url>
</properties>
<activation>
<!--设定是否为默认启动环境-->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!--生产环境-->
<profile>
<id>env_pro</id>
<properties>
<jdbc.url>jdbc:mysql://127.0.0.2:3306/db</jdbc.url>
</properties>
</profile>
<!--测试环境-->
<profile>
<id>env_test</id>
<properties>
<jdbc.url>jdbc:mysql://127.0.0.3:3306/db</jdbc.url>
</properties>
</profile>
</profiles>
私服:
- 私服是一台独立的服务器,用于解决团队内部的资源共享与资源同步问题
- Nexus
四、MybatisPlus
1.简介
MP是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率
2.标准数据层开发
入门案例:
-
导依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> </dependencies>
-
配置yml
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test1 username: root password: 123456 mybatis-plus: global-config: db-config: table-prefix: tb_ id-type: auto #开启主键id自增 configuration: #开启日志详细过程,方便调试 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
制作实体类
-
定义数据接口,继承BaseMapper
使用mybatis-plus进行分页操作:mybatisplus的分页实现其实是借助了拦截器的拦截功能,在查询之前进行了两次拦截,最终完成封装操作
-
写一个mybatis-plus配置类进行拦截器操作
@Configuration public class MPConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }
-
接口测试,执行分页查询
@Test void testPage() { IPage page = new Page(1, 3); bookDao.selectPage(page, null); System.out.println(page.getCurrent());//当前页码 System.out.println(page.getSize());//页面大小,一页多少数据 System.out.println(page.getTotal());//所有记录数 System.out.println(page.getPages());//一共页码数 System.out.println(page.getRecords());//数据总数 }
3.DQL控制
1.条件查询方式
条件查询:使用QueryWrapper或者LambdaQueryWrapper对象封装查询条件
@Test
void testQuery() {
String type = "计算机";
//方式一:按条件查询
QueryWrapper<Book> qw = new QueryWrapper<>();
qw.like(type != null, "type", type);
bookDao.selectList(qw);
//方式二:lambda格式按条件查询(推荐)
QueryWrapper<Book> qw2 = new QueryWrapper<>();
qw.lambda.like(type != null, Book::getType, type);
bookDao.selectList(qw2);
//方式三:lambda格式按条件查询(推荐)
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
lqw.like(type != null, Book::getType, type);
bookDao.selectList(lqw);
}
2.查询投影
查询结果包含模型类中的部分属性:
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
//查询显示部分字段信息
lqw.select(Book::getId, Book::getName, Book::getType);
List<Book> books = bookDao.selectList(lqw);
System.out.println(books);
查询结果包含模型类中未定义的属性:
QueryWrapper<Book> qw = new QueryWrapper<>();
qw.select("count(*) as count,type").groupBy("type");
List<Map<String, Object>> books = bookDao.selectMaps(qw);
System.out.println(books);
3.查询条件设定
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
//等同于=
lqw.eq(Book::getName, "生活大爆炸").eq(Book::getType, "生活百科");
Book book = bookDao.selectOne(lqw);
System.out.println(book);
//范围查询 lt le gt ge eq between
lqw.between(Book::getId,1,10);
List<Book> books = bookDao.selectList(lqw);
System.out.println(books);
//模糊查询 like
lqw.like(Book::getName,"2");
4.字段映射与表名映射
表字段与编码属性设计不同步:@TableField(value=“xxx”)
编码中添加了数据库中未定义的属性:@TableField(exist=false)
采用默认查询开放了更多的字段查看权限:@TableField(value=“xxx”,select=false)
表名与编码开发设计不同步:@TableName(“xxx”)
4.DML控制
1.Insert
id生成策略控制:@TableId(type = IdType.xxx)
2.Delete
按照主键删除多条记录:
List<Long> ids = Arrays.asList(new Long[]{2L, 3L});
bookDao.deleteBatchIds(ids);
根据主键查询多条记录:
List<Long> ids = Arrays.asList(new Long[]{2L, 3L});
List<Book> books = bookDao.selectBatchIds(ids);
逻辑删除:并不是真正的将数据删除而是将数据做上标记
-
实体类中添加对应字段,并设定当前字段为逻辑删除标记字段:@TableLogic(value=“1”,delval=“0”)
-
配置逻辑删除字面值
mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-not-delete-value: 0 logic-delete-value: 1
3.Update
乐观锁:@Version
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//1.定义MP拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//2.添加具体的拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
//3.添加乐观锁拦截器
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
步骤:
- 数据库表中添加锁标记字段
- 实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
- 配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装
- 使用乐观锁机制在修改前必须先获取到对应数据的version方可正常进行
5.快速开发
导入依赖:
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
编写生成代码工具类:
public class CodeGenerator {
//直接运行帮我们生成代码
public static void main(String[] args) {
/**
* 第一步: 使用代码生成器
*/
//怎么用代码生成器呢?
// 1.创建代码生成器对象
// 2.执行代码生成器 //mp包里面的AutoGenerator
AutoGenerator autoGenerator = new AutoGenerator();
// 告诉它怎么生成,在哪生成
//datasource数据源配置
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("123456");
autoGenerator.setDataSource(dataSource);
//会在D盘生成一个com文件,但是这个位置是不对的,需要我们再进一步配置
/**
* 第二步: 设置全局配置
*/
GlobalConfig globalConfig = new GlobalConfig();
//默认输出D盘根下,设置到这一目录下 mybatis_04-generator/src/main/java
globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatis_04-generator/src/main/java");
//globalConfig.setOutputDir(System.getProperty("user.dir")+"\\src\\main\\java");
// \\src\\main\\java
//设置完之后是否打开资源管理器 NO
globalConfig.setOpen(false);
//设置作者
globalConfig.setAuthor("黑马程序员");
//设置是否覆盖原始生成的文件
globalConfig.setFileOverride(true);
//设置数据层接口名,%s为占位符 代表数据库中的表名或模块名
globalConfig.setMapperName("%sDao");
//设置id生成策略
globalConfig.setIdType(IdType.ASSIGN_ID);
autoGenerator.setGlobalConfig(globalConfig);
/**
* 第三步: 设置包名相关配置
*/
PackageConfig packageConfig =new PackageConfig();
//设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
packageConfig.setParent("com.aaa");
//设置实体类包名
packageConfig.setEntity("domain");
//设置数据层包名
packageConfig.setMapper("dao");
autoGenerator.setPackageInfo(packageConfig);
/**
* 第四步: 策略设置
*/
StrategyConfig strategyConfig = new StrategyConfig();
//设置当前参与生成的表名,参数为可变参数 生成指定表
strategyConfig.setInclude("tbl_user");
//设置数据库表的前缀名称,模块名=数据库表名-前缀名 User=tbl_user - tbl_
strategyConfig.setTablePrefix("tbl_");
//是否启用Rest风格
strategyConfig.setRestControllerStyle(true);
//设置乐观锁字段名
strategyConfig.setVersionFieldName("version");
//设置逻辑删除字段名
strategyConfig.setLogicDeleteFieldName("deleted");
//设置是否启用Lombok
strategyConfig.setEntityLombokModel(true);
autoGenerator.setStrategy(strategyConfig);
autoGenerator.execute();//执行
}
}