Spring总结(2/2):代码

0、注解

 

  • @Autowired:2章

 

1、Spring工程结构

2、组件/业务逻辑类/Bean:XxxService.java

2.1、要求

要求

说明

例子

类注解@Component 注解@Component自动定义一个BeanBean名首字母小写类名这个Bean可以在其他Bean中通过@Autowired注入
@Component
public class UserService{ ... }

内部注解@Autowired

写在需要注入的Bean之前,表示注入该Bean被注入的Bean需要有类注解@Component。

这种Bean在定义的时候,不写权限,此时默认权限为package

 
@Component
public class UserService {
    @Autowired
    MailService mailService;//mailService是引入的外部组件

    ...
}
 @Transactional

写在Bean类的业务逻辑方法前,表明该方法需要调用AOP事务。

写在Bean类之前,表明该类的所有public方法都要调用AOP事务。

 
复制代码
@Component
public class UserService {
    // 有事务:
    @Transactional
    public User createUser(String name) {
        ...
    }
    // 无事务:
    public boolean isValidName(String name) {
        ...
    }
}
复制代码
@Component
@Transactional
public class UserService {
    ...
}

 @Autowired

JdbcTemplate jdbcTemplate

数据库操作  
     
     
     

2.2、Bean的一些额外注解

注解

说明

例子

@Scope

原型Bean与单例Bean的说明,区别在于:

容器每次调用getBean(),是否会获得新的实例

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //原型Bean
public class MailSession {
    ...
}

@Autowired

List<XxxBean>xxxBean

 

@Order(n)

注入项目下所有继承自XxxBean的Bean,作为List中的元素,访问时通过for each获取List中的这些Bean。

 

写在上文所说的Bean之前,@Component之后,表明List中这些Bean的次序

复制代码
@Component
public class Validators {
    @Autowired
    List<Validator> validators;

    ...
    for (var validator : this.validators) { 
        ...
    }
}
复制代码
@Autowired(required = false) 对容器说明,找不到Bean时忽略该Bean  
@Bean

注入第三方Bean(非我们自己写的组件)。

需要在配置类(AppConfig)中编写一个方法,返回这个Bean,并为该方法添加@Bean

复制代码
@Configuration
@ComponentScan
public class AppConfig {
    // 创建一个Bean:
    @Bean
    ZoneId createZoneId() {
        return ZoneId.of("Z");
    }
}
复制代码

初始化 @PostConstruct

清理 @PreDestroy

注入Bean后,调用@PostConstruct指定的方法进行初始化;

销毁时,调用@PreDestroy指定的方法进行销毁。

 

实现之前还要引入依赖:

javax.annotation-api

复制代码
@Component
public class MailService {
    @Autowired(required = false)
    ZoneId zoneId = ZoneId.systemDefault();

    @PostConstruct
    public void init() {
        System.out.println("Init mail service with zoneId = " + this.zoneId);
    }

    @PreDestroy
    public void shutdown() {
        System.out.println("Shutdown mail service");
    }
}
复制代码

@Bean("name");

@Bean+@Qualifier("name");

@Bean+@Primary。

指定Bean的别名。

注入时需要用@Qualifier("name")注入指定别名的Bean。

复制代码
//Bean创建时指定别名
public class AppConfig {
    @Bean("z")
    ZoneId createZoneOfZ() {
        return ZoneId.of("Z");
    }
    ...
}

//使用Bean时指定别名
@Component
public class MailService {
    @Autowired(required = false)
    @Qualifier("z") // 指定注入名称为"z"的ZoneId
    ZoneId zoneId = ZoneId.systemDefault();
    ...
}
复制代码

@Profile

 

写在@Bean之后

指示Bean的运行环境,不在指定运行环境下的Bean不会被创建。

开发:native

测试:test

生产production

复制代码
    @Bean
    @Profile("!test")
    ZoneId createZoneId() {
        return ZoneId.systemDefault();
    }

    @Bean
    @Profile("test")
    ZoneId createZoneIdForTest() {
        return ZoneId.of("America/New_York");
    }
复制代码
@Conditional

写在@Component之后

根据条件逻辑决定是否创建Bean

@Component
@Conditional(OnSmtpEnvCondition.class)
public class SmtpMailService implements MailService {
    ...
}

2.3、例子

MailService,负责用户注册和登录成功后的邮件发送

复制代码
@Component
public
class MailService { private ZoneId zoneId = ZoneId.systemDefault(); public void setZoneId(ZoneId zoneId) { this.zoneId = zoneId; } public String getTime() { return ZonedDateTime.now(this.zoneId).format(DateTimeFormatter.ISO_ZONED_DATE_TIME); } public void sendLoginMail(User user) { System.err.println(String.format("Hi, %s! You are logged in at %s", user.getName(), getTime())); } public void sendRegistrationMail(User user) { System.err.println(String.format("Welcome, %s!", user.getName())); } }
复制代码

UserService,用户注册与登录

复制代码
@Component
public
class UserService {   @Autowired
  MailService mailService;
private List<User> users = new ArrayList<>(List.of( // users: new User(1, "bob@example.com", "password", "Bob"), // bob new User(2, "alice@example.com", "password", "Alice"), // alice new User(3, "tom@example.com", "password", "Tom"))); // tom public User login(String email, String password) { for (User user : users) { if (user.getEmail().qualsIgnoreCase(email) && user.getPassword().equals(password)) { mailService.sendLoginMail(user); return user; } } throw new RuntimeException("login failed."); } public User getUser(long id) { return this.users.stream().filter(user -> user.getId() == id).findFirst().orElseThrow(); } public User register(String email, String password, String name) { users.forEach((user) -> { if (user.getEmail().equalsIgnoreCase(email)) { throw new RuntimeException("email exist."); } }); User user = new User(users.stream().mapToLong(u -> u.getId()).max().getAsLong() + 1, email, password, name); users.add(user); mailService.sendRegistrationMail(user); return user; } }
复制代码

2.4、事务类/AOP/Aspect

要求

要求

说明

例子

spring-aspects依赖  
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${spring.version}</version>
</dependency>
@Aspect

写在@Component之前

表明该Bean是一个事务Bean,其中用@Before和@Around标注的方法将自动注入指定的位置执行

@Aspect
@Component
public class LoggingAspect {
    ...
}
@Before

写在Aspect Bean中的某个方法前

表示这个类将在某个位置执行

// 在执行UserService的每个方法前执行:
@Before("execution(public * com.itranswarp.learnjava.service.UserService.*(..))")
public void doAccessCheck() {
    System.err.println("[Before] do access check...");
}
@Around

写在Aspect Bean中的某个方法前

表明这个方法是否执行,以及执行的位置

// 在执行MailService的每个方法前后执行:
@Around("execution(public * com.itranswarp.learnjava.service.MailService.*(..))")
public Object doLogging(ProceedingJoinPoint pjp) throws Throwable {
    System.err.println("[Around] start " + pjp.getSignature());
    Object retVal = pjp.proceed();
    System.err.println("[Around] done " + pjp.getSignature());
    return retVal;
}
@EnableAspectJAutoProxy

写在@Configuration配置类之前

IoC容器看到配置类前的这个注解,就会自动查找带@Aspect的Bean,并将其中的@Around和@Before方法注入到需要注入的Bean中

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AppConfig {
    ...
}
@Transactional

写在Bean类的业务逻辑方法前,表明该方法需要调用AOP事务。

写在Bean类之前,表明该类的所有public方法都要调用AOP事务。

 
复制代码
@Component
public class UserService {
    // 有事务:
    @Transactional
    public User createUser(String name) {
        ...
    }

    // 无事务:
    public boolean isValidName(String name) {
        ...
    }

}
复制代码
@Component
@Transactional
public class UserService {
    ...
}
     
     
     

 

3、Entity类:表示一个业务对象,是业务逻辑类操作的对象

复制代码
public class User {
    private long id;
    private String email;
    private String password;
    private String name;

    public User(long id, String email, String password, String name) {
        this.id = id;
        this.email = email;
        this.password = password;
        this.name = name;
    }

    //getter and setter
    //...
}
复制代码

注解:

  • @Entity,Spring会自动进行Entity类同名SQL表之间的映射

  • @Column:写在Entity类getter方法前,说明属性之间的映射规则

DAO的区别

DAO中定义的是,从数据库根据ID、Name进行查询的具体方法,它的返回结果是Entity类;

而Entity类则是与数据库表相对应的JavaBean,每个属性就是其中的一列。

4、Resource文件

4.1、一般Resource,如文本文件logo.txt

注解:

注解

说明

例子

@Value("classpath:/logo.txt") 注入classpath下的资源文件,一般放在Resource目录下
@Component
public class AppService {
    @Value("classpath:/logo.txt")
    private Resource resource;
    
    ....
}
@Value("file:具体路径/logo.txt") 注入具体路径下的文件作为资源  

 

注入Resource之后,可以通过Resource.getInputStream()获取输出流,避免自己搜索文件的路径:

@PostConstruct
public void init() throws IOException {
    try (var reader = new BufferedReader(
            new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
        this.logo = reader.lines().collect(Collectors.joining("\n"));
    }
}

上文代码中,reader就是一个用注入资源文件构造的BufferedReader,通过该变量的相关方法可以访问文件中的具体事项。

 

4.2、配置文件:.properties.yml

(37条消息) SpringBoot application.properties 使用详解_Full Stack Developme的博客-CSDN博客_application-prod.properties

配置文件是指,各种配置项以Key=Value的形式存放的.properties文件。

4.2.1、文件读取方法:

不用@Value读入文件,而是用@Value读入具体的Key

读取方法

说明

例子

@ProertySource("文件名")

用于AppConfig

读取classpath下的指定文件作为配置文件

@Configuration
@ComponentScan
@PropertySource("app.properties") // 表示读取classpath的app.properties
public class AppConfig {
    ...
}

4.2.2、变量读取方法

读取方法

例子

@Value(XXX),XXX可以是

  • ${Key}:读取Key的Value,如果Key不存在,启动将报错
  • ${Key:DefaultValue}:读取Key的Value,如果Key不存在,就是用默认值DefaultValue

配合@Properties实现

@Value("${app.zone:Z}")
String zoneId;

通过创建一个简单的JavaBean持有各个配置项,持有的方法也是通过${Key}

通过该JavaBean传入配置项时,使用@Value("#{Bean.Key}")

用@Component标注的Xxx,会自动持有Bean名xxx(首字母小写)。

复制代码
//持有配置项的JavaBean
@Component
public class SmtpConfig {
    @Value("${smtp.port:25}")
    private int port;

    public int getPort() {
        return port;
    }
}
复制代码
//通过JavaBean获取配置
@Component
public class MailService {
    @Value("#{smtpConfig.port}")
    private int smtpPort;
}
 

4.2.3、 补充

1)配置文件中的不同Key之间,也可以通过${Key}互相引用

my.name=航歌
my.age=100
my.info=name:${my.name} age:${my.age}

2)通过${random}生成各种类型的随机值

my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}

3)配置文件的优先级

Spring Boot 项目中的 application.properties 配置文件一共可以出现在如下 4 个位置(优先级逐渐降低):

  • 项目根目录下的 config 文件夹
  • 项目根目录下
  • classpath 下的 config 文件夹
  • classpath 下

如果这 4 个地方都有 application.properties 文件,加载的优先级就是从 1 到 4 依次降低,Spring Boot 将按照这个优先级查找配置信息。

 

 

5、Appconfig.java

要求:

要求

说明

例子

@Configuration

表示这是一个配置类  

@ComponentScan

有了这个注解,容器就会自动搜索当前类所在的包和子包,把所有注解@ComponenetBean自动创建出来,并根据@Autowired进行装配。  

@EnableAspectJAutoProxy

自动查找带@Aspect注解的Bean,根据其中的@Before和@Around,将AOP注入指定的Bean中

 

@EnableTransactionManagement

启动声明式事务,启动该项后,自动启动AOP,所以不用再写一遍注解

@EnableAspectJAutoProxy

 

@Bean HikariDataSource

@Bean JdbcTemplate

@Component DatabaseInitializer

@BeanPlatform TransactionManager

JDBC

 

@Bean DataSource

@Bean LocalSessionFactoryBean

@Bean HibernateTemplate

@Bean

HibernateTransactionManager

 Hibernate  

main()

启动Spring事务,调用各业务逻辑类处理业务

 

AppConfig.java必须放在最顶层,与其他Bean的所在子包位于同一目录。

   
上下文环境的配置通过context变量处理 这是一个AnnotationConfigApplicationContext实例,通常放在main方法第一行实现。
ApplicationContext context = new 
          AnnotationConfigApplicationContext(AppConfig.class);
Bean通过context.getBean(XxxService.class)获取

这些Bean必须要用@Component标注,以保证可以扫描到。

且Bean所在的包目录要与AppConfig.java同级。

UserService userService = context.getBean(UserService.class);

@PropertySource("xxx.properties")

@Value("jdbc.url")

获取classpath(一般是resource目录)下的配置文件

通过4.2.1节所说的方式,注入相关配置

 
     
复制代码
@Configuration
@ComponentScan
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new 
            AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        User user = userService.login("bob@example.com", "password");
        System.out.println(user.getName());
    }
}
复制代码

 

6、JDBC

要求

要求

说明

例子

@Bean HikariDataSource

Appconfig中创建的Bean

代表一个DataSource实例,即连接池

复制代码
    @Bean
    DataSource createDataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(jdbcUrl);
        config.setUsername(jdbcUsername);
        config.setPassword(jdbcPassword);
        config.addDataSourceProperty("autoCommit", "true");
        config.addDataSourceProperty("connectionTimeout", "5");
        config.addDataSourceProperty("idleTimeout", "60");
        return new HikariDataSource(config);
    }
复制代码
@Bean JdbcTemplate

Appconfig中创建的Bean

用于操作JDBC

需要在所有需要数据库操作的Bean中,通过@Autowired注入

    @Bean
    JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
@Component DatabaseInitializer

一个外部Bean

在Spring启动时初始化数据库表User(非必须的Bean)

复制代码
@Component
public class DatabaseInitializer {
    @Autowired
    JdbcTemplate jdbcTemplate;

    @PostConstruct
    public void init() {
        jdbcTemplate.update("CREATE TABLE IF NOT EXISTS users (" //
                + "id BIGINT IDENTITY NOT NULL PRIMARY KEY, " //
                + "email VARCHAR(100) NOT NULL, " //
                + "password VARCHAR(100) NOT NULL, " //
                + "name VARCHAR(100) NOT NULL, " //
                + "UNIQUE (email))");
    }
}
复制代码

 

@Autowired
JdbcTemplate jdbcTemplate;
需要数据库操作的业务逻辑类XxxService中注入jdbcTemplate  
@Component
public class DatabaseInitializer {
    @Autowired
    JdbcTemplate jdbcTemplate;
    ...
    //用jdbcTemplate操作数据库
}
@Component
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    ...
    //用jdbcTemplate操作数据库
}

T execute(ConnectionCallback<T> action)

T execute(String sql , PreparedStatementCallback<T> action)

T queryForObject(String sql ,

Object [ ] args , RowMapper<T> rowMapper)

查询,用jdbcTemplate.xxx()调用

具体请参考

Spring总结(1/2)8.2.1节

 
int update(SQL , v1 , v2 , ...) 

增删改,都用该方法

具体请参考Spring总结(1/2)8.2.2节

 
PlatformTransactionManager

Appconfig中的一个Bean

作用是事务管理器

    @Bean
    PlatformTransactionManager createTxManager(@Autowired DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
TransanctionStatus 事务
复制代码
TransactionStatus tx = null;
try {
    // 开启事务:
    tx = txManager.getTransaction(new DefaultTransactionDefinition());
    // 相关JDBC操作:
    jdbcTemplate.update("...");
    jdbcTemplate.update("...");
    // 提交事务:
    txManager.commit(tx);
} catch (RuntimeException e) {
    // 回滚事务:
    txManager.rollback(tx);
    throw e;
}
复制代码
 @Transactional

对于需要开启事务的业务逻辑Bean的方法,需要添加该注解。

直接对业务逻辑Bean添加该注解,表示所有方法都支持事务。

 
@Component
public class UserService {
    // 此public方法自动具有事务支持:
    @Transactional
    public User register(String email, String password, String name) {
       ...
    }
}
@Component
@Transactional
public class UserService {
    ...
}
throw new RuntimeException("info")  事务回滚,只需要在事务方法总抛出RuntimeException(及其子类)  
复制代码
@Transactional
public buyProducts(long productId, int num) {
    ...
    if (store < num) {
        // 库存不够,购买失败:
        throw new IllegalArgumentException("No enough products");
    }
    ...
}
复制代码

@Transactional(rollbackFor = {A.class,B.class,...})

@Transactional(propagation = 级别)

指定在抛出某些具体的Exception时,才回滚事务

定义事务传播级别

@Transactional(rollbackFor = {RuntimeException.class, IOException.class})
public buyProducts(long productId, int num) throws IOException {
    ...
}
自定义业务异常类

继承自RuntimeException

这样可以不必声明任何特殊异常就让声明式事务正常工作

 
复制代码
public class BusinessException extends RuntimeException {
    ...
}

public class LoginException extends BusinessException {
    ...
}

public class PaymentException extends BusinessException {
    ...
}
复制代码

依赖

复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
        <version>1.3.2</version>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
        <version>3.4.2</version>
    </dependency>
    <dependency>
        <groupId>org.hsqldb</groupId>
        <artifactId>hsqldb</artifactId>
        <version>2.5.0</version>
    </dependency>
</dependencies>
复制代码

 

7、Dao

一个经典Dao模型:注意与经典JavaBean的getter、setter区分,这里是数据修改(增删改查)的常用方法

复制代码
public class UserDao{
    @Autowired
    JdbcTemplate jdbcTemplate;
    
    User getById(long id){
        ...
    }
    
    List<User> getUsers(int pages){
        ...
    }

    User createUser(User user){
        ...
    }

    User updateUser(User user){
        ...
    }

    void deleteUser(user user){
        ...
    }
}
复制代码

要求

要求

说明

例子

JdbcDaoSupport

Spring提供的一个Dao模型,是一个抽象类。

子类从它继承后,调用getJdbcTemplate()获得JdbcTemplate实例

复制代码
public abstract class JdbcDaoSupport extends DaoSupport {

    private JdbcTemplate jdbcTemplate;

    public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        initTemplateConfig();
    }

    public final JdbcTemplate getJdbcTemplate() {
        return this.jdbcTemplate;
    }

    ...
}
复制代码
AbstractDao 并非Spring提供,而是自己写的,专门用于注入JdbcTemplate,这也是一个抽象类。
复制代码
public abstract class AbstractDao extends JdbcDaoSupport
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @PostConstruct
    public void init(){
        super.setJdbcTemplate(jdbcTemplate);
    }
}
复制代码
AbstractDao<T>

泛型Dao,其中增添了更多样版代码,同时实现了更多的通用方法如

getById(),getAll(),deleteById()

复制代码
public abstract class AbstractDao<T> extends JdbcDaoSupport{
    private String table;
    private Class<T> entityClass;
    private RowMapper<T> rowMapper;
    
    public AbstractDao(){
        //获取当前类型的泛型类型
        this.entityClass = getParameterizedTpe();
        this.table = this.entityClass.getSimpleName().toLowerCase()+"s";
        this.rowMapper = new BeanPropertyRowMapper<>(entityClass);
    }
    
    public T getById(long id){
        return getJdbcTemplate().queryForObject("SELECT * FROM "+ table + " WHERE id = ? ",this.rowMapper,id);
    }

    public List<T> getAll(int pageIndex){
        int limit = 100;
        int offset = limit * (pageIndex - 1);
        return getJdbcTemplate().query("SELECT * FROM "+ table + " LIMIT ? OFFSET ?", new Object[]{limit,offset},this.rowMapper);
    }

    public void deleteById(long id){
        getJdbcTemplate().update("DELETE FROM "+table+"WHERE id = ?",id);
    }
    ...
}
复制代码

 

XxxDao

真正的Dao类,继承自AbstractDao<Xxx>用于数据处理(增删改查)。

可以直接调用getJdbcTemplate()获得JdbcTemplate实例

 
复制代码
@Component
@Transactional
public class UserDao extends AbstractDao<User>{
    //已经有了:
    //User getById(long)
    //List<User> getAll(int)
    //void deleteById(long)
}
@Component
@Transactional
public class BookDao extends AbstractDao<Book>{
    //已经有了:
    //Book getById(long)
    //List<Book> getAll(int)
    //void deleteById(long)
}
复制代码

@Component

@Transactional

Dao所需的事务注解  

 8、Hibernate

依赖:org.hibernate

复制代码
<!-- JDBC驱动,这里使用HSQLDB -->
<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>2.5.0</version>
</dependency>

<!-- JDBC连接池 -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.4.2</version>
</dependency>

<!-- Hibernate -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.2.Final</version>
</dependency>

<!-- Spring Context和Spring ORM -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
复制代码

要求

要求

说明

代码

@Bean DataSource AppConfig中的一个Bean,表示DataSource(及其子类),即连接池
@Bean
DataSource createDataSource(){
   ...
}
@Bean LocalSessionFactoryBean AppConfig中的一个Bean,用于创建SessionFactory
复制代码
    @Bean
    LocalSessionFactoryBean createSessionFactory(@Autowired DataSource dataSource){
        var props = new Properties();
        props.setProperty("bibernate.hbm2ddl.auto","update");//生产环境不要使用
        props.setProperty("hibernate.dialect","org.hibernate.dialect.HSQLDialect");
        props.setProperty("hibernate.show_sql","true");
        var sessionFactoryBean = new LocalSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);

        //扫描指定的package获取所有的entity class:
        sessionFactoryBean.setPackagesToScan("com.itranswarp.learnjava.entity");
        sessionFactoryBean.setHibernateProperties(props);
        return sessionFactoryBean;
    }
复制代码

 

SessionFactory

每次连接数据库时,用于创建新的Session。

即从连接池中获取一个新的Connection

 
@Bean HibernateTemplate Spring提供的用于简化Hibernate操作的工具类
@Bean
HibernateTemplate createHibernateTemplate(@Autowired SessionFactory sessionFactory){
    return new HibernateTemplate(sessionFactory);
}

@Bean

HibernateTransactionManager

继承自PlatformTransactionManager,按照第6章所说,这是一个事务管理器,用于启动声明式服务
@Bean
PlatformTransactionManager createTxManager(@Autowired SessionFactory sessionFactory){
    return new HibernateTransactionManager(sessionFactory);
}
@Entity

写在数据表对应的JavaBean前的注解。

表示要在Xxx类xxx表之间进行映射。

@Entity
public class User{
    ...
}

@Column(

nullable = false ,

unique = true ,

updatable = false,

length = 100

)

写在JavaBean的getter方法前,表示类字段表中某列间的映射。

nullable:是否允许为NULL;

updatable:该列是否允许UPDATE;

unique:是否唯一不重复;

length:String类型的列每个元素的长度

复制代码
@Column(nullable = false, unique = true, length = 100)
public String getEmail() { ... }

@Column(nullable = false, length = 100)
public String getPassword() { ... }

@Column(nullable = false, length = 100)
public String getName() { ... }

@Column(nullable = false, updatable = false)
public Long getCreatedAt() { ... }
复制代码

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

写在JavaBean的某一个getter方法前,指示主键。

@GeneratedValue标识自增主键。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, updatable = false)
public Long getId() { ... }

 

这个JavaBean的getter的返回值必须是包装类型
不能是基本类型  
@MappedSuperclass 标注AbstractEntity,这是提取不同JavaBean的共同属性构成的一个抽象Entity类,专门用于继承。
复制代码
@MappedSuperclass
public abstract class AbstractEntity{
    private Long id;
    private Long createdAt;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false , updatable = false)
    public Long getId() {...}

    @Column(nullable = false, updatable = false)
    public Long getCreatedAt() { ... }
}
复制代码

 

@Transient

用于某个getter方法前,表明该方法并非从数据库提取属性的值,而是通过计算获得。

需要引入依赖

javax.persistence

@Transient
public ZonedDateTime getCreatedDateTime(){
    return Instant.ofEpochMilli(this.createAt).atZone(ZoneId.systemDefault());
}
@Persist

标注某个普通方法。

该方法将在JavaBean持久化到数据库之前执行。

需要引入依赖

javax.persistence

@PrePersist
public void preInsert(){
    setCreatedAt(System.currentTimeMillis());
}
从AbstractEntity继承而来的Entity

一个Entity,继承自@MappedSuperclass,只需要写特有字段的getter方法即可

复制代码
复制代码
@Entity
public class User extends AbstractEntity {

  @Column(nullable = false, unique = true, length = 100)
  public String getEmail() { ... }

  @Column(nullable = false, length = 100)
  public String getPassword() { ... }

  @Column(nullable = false, length = 100)
  public String getName() { ... }
}
复制代码
复制代码

利用Hibernate对user表进行增删改查(步骤):

①往业务逻辑类XxxService中,注入HibernateTemplate:

@Component
@Transactional
public class UserService{
    @Autowired
    HibernateTemplate hibernateTemplate;
}

②持久化/INSERT:hibernateTemplate.save(user);

持久化的意思是,把一个JavaBean实例,作为表的一行插入表中。

复制代码
public User register(String email, String password, String name) {
    // 创建一个User对象:
    User user = new User();
    // 设置好各个属性:
    user.setEmail(email);
    user.setPassword(password);
    user.setName(name);
    // 不要设置id,因为使用了自增主键
    // 保存到数据库:
    hibernateTemplate.save(user);
    // 现在已经自动获得了id:
    System.out.println(user.getId());
    return user;
}
复制代码

③删除/DELETE:get(User.class,id) + delete(user)

根据ID进行删除

public boolean deleteUser(Long id) {
    User user = hibernateTemplate.get(User.class,id);
    if (user != null){
        hibernateTemplate.delete(user);
        return true;
    }
    return false;
}

④更新/UPDATE:load(User.class,id) + update(user)

先根据ID获取某行记录作为一个user,再对该user进行更新:

public void updateUser(Long id, String name) {
    User user = hibernaateTemplate.load(User.class , id);
    user.setName(name);
    hibernateTemplate.update(user);
}

⑤条件查询WHERE

SELECT * FROM user WHERE email = ? AND password = ?

a、Example查询:findByExample(user)

给出一个User实例,Hibernate把该实例的所有非null属性拼成WHERE条件

public User login(String email , String password){
    User example = new User();
    example.setEmail(email);
    example.setPassword(password);
    List<User> list = hibernateTemplate.findByExample(example);
    return list.isEmpty() ? null : list.get(0);
}

因为example实例只有emailpassword两个属性为非null,所以最终生成的WHERE语句为WHERE email = ? AND password = ?

基本类型字段总是会加入WHERE条件!

b、Criteria查询:findByCriteria(DetachedCriteria

public User login(String email, String password) {
    DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
    criteria.add(Restrictions.eq("email", email))
            .add(Restrictions.eq("password", password));
    List<User> list = (List<User>) hibernateTemplate.findByCriteria(criteria);
    return list.isEmpty() ? null : list.get(0);
}

findByExample()相比,findByCriteria()可以组装出更加灵活的WHERE条件,例如:

SELECT * FROM user WHERE (email = ? OR name = ?) AND password = ?

该查询没法用findByExample()实现,用Criteria查询可以实现如下:

复制代码
DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
criteria.add(
    Restrictions.and(
        Restrictions.or(
            Restrictions.eq("email", email),
            Restrictions.eq("name", email)
        ),
        Restrictions.eq("password", password)
    )
);
复制代码

只要组织好Restrictions的嵌套关系,Criteria查询可以实现任意复杂的查询。

c、HQL查询:find(SQL,para1,para2,...)

最后一种常用的查询是直接编写Hibernate内置的HQL查询:

String SQL= "FROM User WHERE email = ? AND password = ?";
List<User> list = (List<User>) hibernateTemplate.find(SQL,email,password);

和SQL相比,HQL使用类名和属性名,由Hibernate自动转换为实际表名和列名,详细的HQL语法可以参考Hibernate文档

d、@NamedQuires:findByNamedQuery(Queryname,para1,para2,...)

javax.persistence.NamedQuery

查询起个名字,然后保存在注解中。

使用NamedQuery时,我们要现在User类标注:

复制代码
@NamedQueries(
    @NamedQuery(
        // 查询名称:
        name = "login",
        // 查询语句:
        query = "SELECT u FROM User u WHERE u.email=?0 AND u.password=?1"
    )
)
@Entity
public class User extends AbstractEntity {
    ...
}
复制代码

与HQL的区别在于,占位符使用?0?1,并且索引是从0开始的。

使用NamedQuery用findByNamedQuery(Queryname,para1,para2,...)只需要引入查询名参数

public User login(String email , String password){
    List<User> list = (List<User>) hibernateTemplate.findByNamedQuery("login",email,password);
    return list.isEmpty() ? null : list.get(0)l;
}

 

9、JPA

依赖:javax.persistence

  • org.springframework:spring-context:5.2.0.RELEASE
  • org.springframework:spring-orm:5.2.0.RELEASE
  • javax.annotation:javax.annotation-api:1.3.2
  • org.hibernate:hibernate-core:5.4.2.Final
  • com.zaxxer:HikariCP:3.4.2
  • org.hsqldb:hsqldb:2.5.0

JPA是个接口,需要选择实现类(JPA提供方)Hibernate、EclipseLink。本节以Hibernate作为JPA实现

要求:

要求

说明

例子

 @Bean DataSource AppConfig中的一个Bean,表示DataSource(及其子类),即连接池
@Configuration
@ComponentScan
@EnableTransactionManagement
@PropertySource("jdbc.properties")
public class AppConfig{
    @Bean
    DataSource createDataSource(){ ... }
}

 

@Bean 

LocalContainerEntityManagerFactoryBean

AppConfig中的一个Bean,用于创建

EntityManagerFactory

 

 @Bean 

PlatformTransactionManager

AppConfig中的一个Bean,用于声明式事务

通常返回JpaTransactionManager的实例

 
Spring + Hibernate实现JPA时,不用XML     

@Component

@Transactional

用于业务逻辑类XxxService之前

@Component
@Transactional
public class UserService{
    @PersistenceContext
    EntityManager em;
}
@PersistenceContext 用于EntityManager之前,表示注入
EntityManager 

业务逻辑类XxxService中注入的实例,代表一个Connection。

通过该实例的各项方法实现对数据库的操作

@Entity

写在Entity类之前,表示具体的表格对象,如User

@Entity
public class User {
    ...
}

 

JPQL查询

语法和HQL差不多:

  • TypeQuery<User> em.createQuery(SQL,User.class)

  • query.setParameter(占位符,para1,para2,...)

  • List<User> query.getResultList()

占位符:占位符的形式

复制代码
public User getUserByEmail(String email) {
    // JPQL查询:
    String SQL = "SELECT u FROM User u WHERE u.email = :e";
    TypedQuery<User> query = em.createQuery(SQL, User.class);
    query.setParameter("e", email);
    List<User> list = query.getResultList();
    if (list.isEmpty()) {
        throw new RuntimeException("User not found by email.");
    }
    return list.get(0);
}
复制代码

 

NamedQuery查询

定义

通过注解标注在User类前(非XxxService前

复制代码
@NamedQueries(
    @NamedQuery(
        name = "login",
        query = "SELECT u FROM User u WHERE u.email=:e AND u.password=:p"
    )
)
@Entity
public class User {
    ...
}
复制代码

操作数据库

对数据库进行增删改操作,可以分别使用persist()、remove()、merge()方法,参数均为Entity Bean本身。

 

10、Mybatis

依赖:

  • org.mybatis:mybatis:3.5.4
  • org.mybatis:mybatis-spring:2.0.4

要求:

要求

说明

例子

@Bean DataSource AppConfig中的Bean,作用同前  
@Bean SqlSessionFactoryBean AppConfig中的Bean,Mybatis的核心  
@Bean PlatformTransactionManager

AppConfig中的Bean,是一个

DataSourceTransactionManager实例

代表事务管理器

 
interface UserMapper

一个接口,用于实现Entity间的映射

其中不仅要定义访问user表接口方法,还要明确写出查询的SQL语句

public interface UserMapper{
    @Select("SELECT * FROM users WHERE id = #{id}")
    User getById(@Param("id") long id);
}
@Select 接口Mapper中定义的查询SQL语句的注解。 
@Insert 接口Mapper中定义的插入SQL语句的注解。 
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
@Insert("INSERT INTO users (email, password, name, createdAt) VALUES (#{user.email}, #{user.password}, #{user.name}, #{user.createdAt})")
void insert(@Param("user") User user);
@Options  插入后,如果希望获得自增主键,则添加该注解。
@Update 接口Mapper中定义的更新SQL语句的注解。   
@Update("UPDATE users SET name = #{user.name}, createdAt = #{user.createdAt} WHERE id = #{user.id}")
void update(@Param("user") User user);
@Delete  接口Mapper中定义的删除SQL语句的注解。 
@Delete("DELETE FROM users WHERE id = #{id}")
void deleteById(@Param("id") long id);
@MapperScan

写于AppConfig之前

根据该注解,Mybatis会自动扫描指定包,并创建Mapper实现类(通过注入使用)

@MapperScan("com.itranswarp.learn.java.mapper")
...其他注解...
public class AppConfig{
    ...
}

@Autowired

UserMapper userMapper

写在XxxService中

注入之前所写的Mapper,使用其中的各种方法

复制代码
@Component
@Transactional
public class UserService {
    // 注入UserMapper:
    @Autowired
    UserMapper userMapper;

    public User getUserById(long id) {
        // 调用Mapper方法:
        User user = userMapper.getById(id);
        if (user == null) {
            throw new RuntimeException("User not found by id.");
        }
        return user;
    }
}
复制代码
     
posted @   ShineLe  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示