layout | title | date | categories | tags | series | series_index | comments | copyrights |
---|---|---|---|---|---|---|---|---|
post |
MyBatis |
2024-02-06 00:00:00 +0800 |
编程 |
java spring |
深入 Spring 源码 |
6 |
true |
原创 |
Spring 是一个开源的轻量级 JavaEE 框架。它的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 的 IoC 容器负责管理 JavaBean 的生命周期,而 AOP 容器负责管理切面。Spring 还提供了一系列的模块,如 Spring MVC、Spring JDBC、Spring Security 等。
在实际开发中,我们经常会遇到这样的情况:需要操作数据库,但是 JDBC 太底层,Spring JDBC 太繁琐。MyBatis 就是为了解决这些问题而生的。
MyBatis 是一个持久层框架,它将 SQL 语句和 Java 对象映射起来,使得操作数据库更加方便。
MyBatis 使用 XML 文件或者注解来配置 SQL 语句。我们这里只介绍注解配置方法。
首先,我们需要在配置文件中配置数据源:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/book
username: root
password: password
然后,我们需要配置 MyBatis:
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
return factoryBean.getObject();
}
}
然后,我们需要定义一个映射器接口:
@Mapper
public interface BookMapper {
@Select("SELECT * FROM book WHERE id = #{id}")
Book getBookById(int id);
@Insert("INSERT INTO book VALUES(#{id}, #{title}, #{author})")
void addBook(Book book);
}
这个接口中定义了两个方法,一个用于查询,一个用于插入。它们分别使用了 @Select
和 @Insert
注解。类似的,我们还可以使用 @Update
、@Delete
注解。注解中的 SQL 语句使用 #{}
或者 ${}
来引用参数。值得注意的是,#{}
使用的是占位符,通过预编译的方式来防止 SQL 注入,而 ${}
直接拼接参数,容易导致 SQL 注入。
然后,我们就可以使用映射器接口来操作数据库:
@Service
public class BookService {
@Autowired
private BookMapper bookMapper;
public Book getBookById(int id) {
return bookMapper.getBookById(id);
}
public void addBook(Book book) {
bookMapper.addBook(book);
}
}
这样,我们就实现了一个简单的 MyBatis。
MyBatis 还有很多高级用法,例如:
-
动态 SQL
@SelectProvider(type = BookSqlProvider.class, method = "getBook") Book getBook(int id, String title);
public class BookSqlProvider { public String getBook(int id, String title) { return new SQL() {% raw %}{{{% endraw %} SELECT("*"); FROM("book"); if (id != 0) { WHERE("id = #{id}"); } if (title != null) { WHERE("title = #{title}"); } {% raw %}}}{% endraw %}.toString(); } }
它可以支持
if
、choose
、when
、otherwise
、trim
、where
、set
、foreach
等标签。 -
批量操作
@InsertProvider(type = BookSqlProvider.class, method = "addBooks") void addBooks(List<Book> books);
public class BookSqlProvider { public String addBooks(Map<String, List<Book>> map) { List<Book> books = map.get("books"); StringBuilder sql = new StringBuilder(); sql.append("INSERT INTO book VALUES "); for (Book book : books) { sql.append("(").append(book.getId()).append(", '").append(book.getTitle()).append("', '").append(book.getAuthor()).append("'), "); } sql.delete(sql.length() - 2, sql.length()); return sql.toString(); } }
-
缓存
@CacheNamespace public interface BookMapper { @Select("SELECT * FROM book WHERE id = #{id}") @Options(useCache = true) Book getBookById(int id); }
它可以支持
@CacheNamespace
、@Options
注解。当我们添加了
@CacheNamespace
注解后,MyBatis 会自动缓存查询结果,当我们再次查询相同的数据时,MyBatis 会直接从缓存中获取数据,而不会再次查询数据库。
MyBatis Plus 是 MyBatis 的增强工具,它提供了很多增强功能,例如:
-
代码生成器
MyBatis Plus 提供了代码生成器,可以根据数据库表生成实体类、映射器接口、XML 文件。
public class CodeGenerator { public static void main(String[] args) { AutoGenerator generator = new AutoGenerator(); generator.setGlobalConfig(new GlobalConfig().setOutputDir(System.getProperty("user.dir") + "/src/main/java")); generator.setDataSource(new DataSourceConfig().setUrl("jdbc:mysql://localhost:3306/book").setUsername("root").setPassword("password")); generator.setPackageInfo(new PackageConfig().setParent("com.example").setModuleName("book")); generator.setStrategy(new StrategyConfig().setInclude("book")); generator.execute(); } }
运行这个代码,就可以生成实体类、映射器接口、XML 文件。
-
分页插件
MyBatis Plus 提供了分页插件,可以方便地进行分页查询。
@GetMapping("/book") public Page<Book> list(@RequestParam("page") int page, @RequestParam("size") int size) { return bookService.listBooks(page, size); }
@Service public class BookService { @Autowired private BookMapper bookMapper; public Page<Book> listBooks(int page, int size) { return bookMapper.selectPage(new Page<>(page, size), null); } }
这里,我们使用了
Page
类来表示分页查询结果。MyBatis Plus 会自动查询总数,并返回分页查询结果。 -
逻辑删除
MyBatis Plus 提供了逻辑删除功能,可以方便地进行逻辑删除。
@TableLogic private Integer deleted;
@Delete("DELETE FROM book WHERE id = #{id}") void deleteBookById(int id);
@Update("UPDATE book SET deleted = 1 WHERE id = #{id}") void deleteBookById(int id);
这里,我们使用了
@TableLogic
注解来表示逻辑删除字段。MyBatis Plus 会自动将逻辑删除字段加入到查询条件中。 -
多租户
MyBatis Plus 提供了多租户功能,可以方便地进行多租户查询。
@MultiTenant private String tenantId;
@Select("SELECT * FROM book WHERE tenant_id = #{tenantId}") List<Book> listBooks(String tenantId);
@InterceptorIgnore(tenantLine = "true") @Select("SELECT * FROM book") List<Book> listBooks();
这里,我们使用了
@MultiTenant
注解来表示多租户字段。MyBatis Plus 会自动将多租户字段加入到查询条件中。
在实际开发中,我们经常会遇到这样的情况:需要对用户进行认证、授权。如果没有框架,我们需要自己处理这些事情,这样会导致代码冗余、耦合度高、不利于维护。Spring Security 就是为了解决这些问题而生的。
Spring Security 是一个安全框架,它提供了认证、授权、攻击防护等功能,使得应用程序更加安全。
Spring Security 使用 Java 配置来配置项目:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").defaultSuccessUrl("/").permitAll()
.and()
.logout().permitAll();
}
}
这个配置类继承了 WebSecurityConfigurerAdapter
类,它重写了 configure
方法,用于配置认证和授权。
configure(AuthenticationManagerBuilder auth)
方法用于配置认证,我们可以使用 auth.userDetailsService()
方法来配置用户认证服务,使用 auth.inMemoryAuthentication()
方法来配置内存用户认证服务。
configure(HttpSecurity http)
方法用于配置授权,我们可以使用 http.authorizeRequests()
方法来配置请求授权,使用 http.formLogin()
方法来配置表单登录,使用 http.logout()
方法来配置退出登录。