1. Spring整合JDBC

表:

CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '姓名',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
1
2
3
4
5
6
7
INSERT INTO `user`(`id`, `name`, `age`, `email`) VALUES (1, 'Jone', 18, 'test1@mszlu.com');
INSERT INTO `user`(`id`, `name`, `age`, `email`) VALUES (2, 'Jack', 20, 'test2@mszlu.com');
INSERT INTO `user`(`id`, `name`, `age`, `email`) VALUES (3, 'Tom', 28, 'test3@mszlu.com');
INSERT INTO `user`(`id`, `name`, `age`, `email`) VALUES (4, 'Sandy', 21, 'test4@mszlu.com');
INSERT INTO `user`(`id`, `name`, `age`, `email`) VALUES (5, 'Billie', 24, 'test5@mszlu.com');

1
2
3
4
5
6

Spring提供了JdbcTemplate方便我们操作JDBC,从名字上看,属于模板设计模式,在使用的时候,需要先将JdbcTemplate实例化(交给Spring容器管理)

步骤:

  1. 创建Maven工程,导入以下依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.16.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.16.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>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>    
        </dependency>
       <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
    </dependencies>
    
    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
  2. 新建JDBCConfig,定义DataSource和JdbcTemplate

    package com.mszlu.config;
    
    import com.zaxxer.hikari.HikariConfig;
    import com.zaxxer.hikari.HikariDataSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    import javax.sql.DataSource;
    
    @Configuration
    public class JDBCConfig {
    
        @Bean
        public DataSource dataSource(){
            HikariConfig hikariConfig = new HikariConfig();
            hikariConfig.setPassword("root");
            hikariConfig.setUsername("root");
            hikariConfig.setJdbcUrl("jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC");
            hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
            hikariConfig.addDataSourceProperty("autoCommit", "true");
            hikariConfig.addDataSourceProperty("connectionTimeout", "5");
            hikariConfig.addDataSourceProperty("idleTimeout", "60");
            return new HikariDataSource(hikariConfig);
        }
    
        @Bean
        public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource){
            return new JdbcTemplate(dataSource);
        }
    }
    
    
    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
  3. 创建UserService利用JdbcTemplate实现数据库增删改查

    package com.mszlu.service;
    
    import com.mszlu.pojo.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.support.GeneratedKeyHolder;
    import org.springframework.jdbc.support.KeyHolder;
    import org.springframework.stereotype.Service;
    
    import java.sql.PreparedStatement;
    import java.sql.Statement;
    import java.util.List;
    import java.util.Map;
    
    @Service
    public class UserService {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
    
        public void save(String name,Integer age,String email){
            KeyHolder holder = new GeneratedKeyHolder();
            int update = jdbcTemplate.update((conn) -> {
                PreparedStatement ps = conn.prepareStatement("insert into user (name,age,email) values(?,?,?)", Statement.RETURN_GENERATED_KEYS);
                ps.setObject(1, name);
                ps.setObject(2, age);
                ps.setObject(3, email);
                return ps;
    
            }, holder);
            if (update > 0){
                System.out.println("保存成功...user id:"+holder.getKey());
            }
        }
    
        /**
         * 根据用户id 查询
         * @param id
         * @return
         */
        public User getUser(Long id){
            User user = jdbcTemplate.queryForObject("select * from user where id = ?", new Object[]{id},new BeanPropertyRowMapper<>(User.class));
            return user;
        }
    
        /**
         * 分页查询
         * @param page
         * @param pageSize
         * @return
         */
        public List<User> queryUserList(int page,int pageSize){
            int index = (page-1)*pageSize;
            int size = pageSize;
            List<User> list = jdbcTemplate.query("select * from user limit ?,?", new Object[]{index, size}, new BeanPropertyRowMapper<>(User.class));
            return list;
        }
    
        /**
         * 更新
         * @param id
         * @param name
         * @return
         */
        public boolean update(Long id,String name){
            int update = jdbcTemplate.update("update user set name=? where id=?", name, id);
            if (update > 0){
                return true;
            }
            throw new RuntimeException("update error");
        }
    
        public boolean delete(Long id){
            int deleteCount = jdbcTemplate.update("delete from user where id=?", id);
            return deleteCount > 0;
        }
    }
    
    
    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
    package com.mszlu.pojo;
    
    import lombok.Data;
    
    @Data
    public class User {
    
        private Long id;
    
        private String name;
    
        private Integer age;
    
        private String email;
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  4. 测试

    package com.mszlu.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ComponentScan("com.mszlu")
    public class SpringConfig {
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.mszlu;
    
    import com.mszlu.config.SpringConfig;
    import com.mszlu.pojo.User;
    import com.mszlu.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import java.util.List;
    
    public class App {
    
        public static void main(String[] args) {
            ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    
            UserService userService = context.getBean(UserService.class);
    
    //        userService.save("testsave",50,"testsave@mszlu.com");
    //        User user = userService.getUser(1L);
    //        System.out.println(user);
    //        List<User> users = userService.queryUserList(1, 2);
    //        System.out.println(users);
    
    //        boolean updated = userService.update(1L, "hahaha");
    //        System.out.println(updated);
            boolean delete = userService.delete(6L);
            System.out.println(delete);
        }
    }
    
    
    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
  5. execute的用法

    public List<User> execute(int page,int pageSize){
            return jdbcTemplate.execute("select * from user limit ?,?",(PreparedStatement ps)->{
                ps.setObject(1,(page-1)*pageSize);
                ps.setObject(2,pageSize);
                ResultSet resultSet = ps.executeQuery();
                List<User> userList = new ArrayList<>();
                while (resultSet.next()){
                    User user = new User();
                    user.setId(resultSet.getLong("id"));
                    user.setName(resultSet.getString("name"));
                    user.setEmail(resultSet.getString("email"));
                    user.setAge(resultSet.getInt("age"));
                    userList.add(user);
                }
                return userList;
            });
        }
        
        public List<User> execute1(int page,int pageSize){
            return jdbcTemplate.execute((Connection conn)->{
                PreparedStatement ps = conn.prepareStatement("select * from user limit ?,?");
                ps.setObject(1,(page-1)*pageSize);
                ps.setObject(2,pageSize);
                ResultSet resultSet = ps.executeQuery();
                List<User> userList = new ArrayList<>();
                while (resultSet.next()){
                    User user = new User();
                    user.setId(resultSet.getLong("id"));
                    user.setName(resultSet.getString("name"));
                    user.setEmail(resultSet.getString("email"));
                    user.setAge(resultSet.getInt("age"));
                    userList.add(user);
                }
                return userList;
            });
        }
    
    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

2. Spring整合Mybatis

mybatis提供整合spring的包mybatis-spring,通过SqlSession来进行数据库的操作,整合的时候需要定义SqlSessionFactoryBean和MapperScannerConfigurer,其中MapperScannerConfigurer需要配置Mapper接口的扫包路径,用户将此包下的Mapper接口生成代理实现类,并将其注册到Spring容器中。

导包:

		<!--spring整合mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.3</version>
        </dependency>
        <!--mybatis环境-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.3</version>
        </dependency>
<!--分页插件坐标-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.1</version>
        </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

步骤:

  1. 定义DataSource,上一步JDBC中已经定义,下面是xml的配置方式

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           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
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/springboot?characterEncoding=utf8&amp;serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="autoCommit" value="true"/>
            <property name="connectionTimeout" value="5000" />
            <property name="idleTimeout" value="60" />
        </bean>
        <!--jdbc的xml配置-->
        <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
            <constructor-arg name="configuration" ref="hikariConfig"/>
        </bean>
    </beans>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
  2. 定义SqlSessionFactoryBean和MapperScannerConfigurer

    1. 注解

      package com.mszlu.config;
      
      import com.github.pagehelper.PageInterceptor;
      import org.mybatis.spring.SqlSessionFactoryBean;
      import org.mybatis.spring.mapper.MapperScannerConfigurer;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.core.io.ClassPathResource;
      import org.springframework.core.io.Resource;
      import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
      import org.springframework.core.io.support.ResourcePatternResolver;
      
      import javax.sql.DataSource;
      import java.io.IOException;
      import java.util.Properties;
      
      @Configuration
      public class MybatisConfig {
      
          @Bean
          public SqlSessionFactoryBean sqlSessionFactoryBean(@Autowired DataSource dataSource) throws IOException {
              SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
              sqlSessionFactoryBean.setDataSource(dataSource);
              ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
              Resource[] resources = resourceResolver.getResources("classpath*:mapper/*.xml");
              sqlSessionFactoryBean.setMapperLocations(resources);
              //分页插件
              PageInterceptor pageInterceptor = new PageInterceptor();
              Properties properties = new Properties();
              properties.setProperty("helperDialect","mysql");
              pageInterceptor.setProperties(properties);
              sqlSessionFactoryBean.setPlugins(pageInterceptor);
              return sqlSessionFactoryBean;
          }
      
          @Bean
          public MapperScannerConfigurer mapperScannerConfigurer(){
              MapperScannerConfigurer configurer = new MapperScannerConfigurer();
              configurer.setBasePackage("com.mszlu.mapper");
              return configurer;
          }
      }
      
      
      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
    2. xml配置

       <!--mybatis-->
          <bean class="org.mybatis.spring.SqlSessionFactoryBean">
              <property name="dataSource" ref="dataSource" />
              <property name="mapperLocations" value="classpath*:mapper/*.xml"/>
              <property name="plugins">
                  <array>
                      <bean class="com.github.pagehelper.PageInterceptor">
                          <property name="properties">
                              <props>
                                  <prop key="helperDialect">mysql</prop>
                              </props>
                          </property>
                      </bean>
                  </array>
              </property>
          </bean>
          <!--配置mapper接口的扫包路径,目的是代理此接口,生成代理类 注册到spring容器中-->
          <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
              <property name="basePackage" value="com.mszlu.mapper"/>
          </bean>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
  3. 写Mapper接口

    package com.mszlu.mapper;
    
    import com.github.pagehelper.Page;
    import com.mszlu.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
    
        int save(User user);
    
        int update(User user);
    
        User findById(Long id);
    
        Page<User> findAll();
    
        int delete(Long id);
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  4. 在resources下建mapper目录,在mapper下创建UserMapper.xml

    <?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">
    <mapper namespace="com.mszlu.mapper.UserMapper">
    
        <insert id="save" parameterType="com.mszlu.pojo.User" keyProperty="id" useGeneratedKeys="true">
            insert into user (name,age,email) values (##{name},##{age},##{email})
        </insert>
        <update id="update" parameterType="com.mszlu.pojo.User">
            update user
            <set>
                <if test="name != null and name.length>0">
                    name=##{name},
                </if>
                <if test="age != null">
                    age=##{age},
    ,            </if>
                <if test="email != null and email.length>0">
                    email=##{email}
                </if>
            </set>
            where id=##{id}
        </update>
        <delete id="delete" parameterType="long">
            delete from user where id=##{id}
        </delete>
        <select id="findById" parameterType="long" resultType="com.mszlu.pojo.User">
            select * from user where id=##{id}
        </select>
        <select id="findAll" resultType="com.mszlu.pojo.User">
            select * from user
        </select>
    </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
    26
    27
    28
    29
    30
    31
    32
    33
    34
  5. Service的Mybatis实现,增删改查

    package com.mszlu.service;
    
    import com.github.pagehelper.Page;
    import com.github.pagehelper.PageHelper;
    import com.github.pagehelper.PageInfo;
    import com.mszlu.mapper.UserMapper;
    import com.mszlu.pojo.User;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.support.GeneratedKeyHolder;
    import org.springframework.jdbc.support.KeyHolder;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.sql.PreparedStatement;
    import java.sql.Statement;
    import java.util.List;
    
    @Service
    public class UserMybatisService {
    
        @Resource
        private UserMapper userMapper;
    
        public void save(String name,Integer age,String email){
            User user = new User();
            user.setAge(age);
            user.setEmail(email);
            user.setName(name);
            this.userMapper.save(user);
            System.out.println("新增成功,user id: " + user.getId());
        }
    
        /**
         * 根据用户id 查询
         * @param id
         * @return
         */
        public User getUser(Long id){
            return this.userMapper.findById(id);
        }
    
        /**
         * 分页查询
         * @param page
         * @param pageSize
         * @return
         */
        public PageInfo<User> queryUserList(int page, int pageSize){
            PageHelper.startPage(page,pageSize);
            Page<User> userList = this.userMapper.findAll();
            return new PageInfo<>(userList);
        }
    
        /**
         * 更新
         * @param id
         * @param name
         * @return
         */
        public boolean update(Long id,String name){
            User user = new User();
            user.setId(id);
            user.setName(name);
            return this.userMapper.update(user) > 0;
        }
    
        public boolean delete(Long id){
            return this.userMapper.delete(id) > 0;
        }
    }
    
    
    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
  6. 测试

    package com.mszlu;
    
    
    import com.github.pagehelper.PageInfo;
    import com.mszlu.config.SpringConfig;
    import com.mszlu.pojo.User;
    import com.mszlu.service.UserMybatisService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import java.util.List;
    
    public class MybatisApp {
    
        public static void main(String[] args) {
            ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
            UserMybatisService userMybatisService = context.getBean(UserMybatisService.class);
    
            userMybatisService.save("testsave",50,"testsave@mszlu.com");
    
            User user = userMybatisService.getUser(1L);
            System.out.println(user);
    
            PageInfo<User> userList = userMybatisService.queryUserList(1, 2);
            System.out.println(userList);
            boolean update = userMybatisService.update(7L, "hhhhh");
            System.out.println("update:"+update);
            boolean delete = userMybatisService.delete(7L);
            System.out.println("delete:"+delete);
        }
    }
    
    
    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

3. Spring整合Hibernate

Hibernate是另一个较为广泛使用的orm框架

导包:

<!-- Hibernate -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.2.Final</version>
</dependency>
  <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.2.16.RELEASE</version>
        </dependency>
1
2
3
4
5
6
7
8
9
10
11

配置文档:https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html##configurations

使用步骤:

  1. DataSource配置,上面的代码中已经配置

  2. 创建一个LocalSessionFactoryBean,它会再自动创建一个SessionFactory,在Hibernate中,Session是封装了一个JDBC Connection的实例,而SessionFactory是封装了JDBC DataSource的实例,即SessionFactory持有连接池,每次需要操作数据库的时候,SessionFactory创建一个新的Session,相当于从连接池获取到一个新的ConnectionSessionFactory就是Hibernate提供的最核心的一个对象,但LocalSessionFactoryBean是Spring提供的为了让我们方便创建SessionFactory的类。

    package com.mszlu.config;
    
    import org.hibernate.FlushMode;
    import org.hibernate.SessionFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.orm.hibernate5.HibernateTemplate;
    import org.springframework.orm.hibernate5.HibernateTransactionManager;
    import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.persistence.FlushModeType;
    import javax.sql.DataSource;
    import java.util.Properties;
    
    @Configuration
    @EnableTransactionManagement(proxyTargetClass = true)
    public class HibernateConfig {
    
        @Bean
        LocalSessionFactoryBean createSessionFactory(@Autowired DataSource dataSource) {
            Properties props = new Properties();
            props.setProperty("hibernate.hbm2ddl.auto", "update");
            props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL57Dialect");
            props.setProperty("hibernate.show_sql", "true");
            props.setProperty("hibernate.dialect.storage_engine","innodb");
            LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
            sessionFactoryBean.setDataSource(dataSource);
            // 扫描指定的package获取所有entity class:
            sessionFactoryBean.setPackagesToScan("com.mszlu.pojo.hibernate");
            sessionFactoryBean.setHibernateProperties(props);
            return sessionFactoryBean;
        }
    
        @Bean
        HibernateTemplate hibernateTemplate(@Autowired SessionFactory sessionFactory) {
            return new HibernateTemplate(sessionFactory);
        }
        //hibernate 不使用事务 没法玩
        @Bean
        PlatformTransactionManager transactionManager(@Autowired SessionFactory sessionFactory) {
            return new HibernateTransactionManager(sessionFactory);
        }
    }
    
    
    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
  3. 创建HibernateTemplate

      @Bean
        HibernateTemplate hibernateTemplate(@Autowired SessionFactory sessionFactory) {
            return new HibernateTemplate(sessionFactory);
        }
    
    1
    2
    3
    4
  4. 添加注解,告诉Hibernate将User映射到表记录

    package com.mszlu.pojo.hibernate;
    
    import lombok.Data;
    
    import javax.persistence.*;
    
    @Data
    @Entity
    @Table(name = "user")
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(nullable = false,updatable = false)
        private Long id;
        @Column(nullable = false,length = 30)
        private String name;
    
        @Column
        private Integer age;
        @Column
        private String email;
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
  5. Service实现增删改查

    package com.mszlu.service;
    
    import com.mszlu.pojo.hibernate.User;
    import org.hibernate.criterion.DetachedCriteria;
    import org.hibernate.criterion.Restrictions;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.orm.hibernate5.HibernateTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.List;
    
    
    @Service
    @Transactional
    public class UserHibernateService {
    
        @Autowired
        HibernateTemplate hibernateTemplate;
    
        public void save(String name,Integer age,String email){
            User user = new User();
            user.setAge(age);
            user.setEmail(email);
            user.setName(name);
            hibernateTemplate.save(user);
            System.out.println("新增成功,user id: " + user.getId());
        }
    
        /**
         * 根据用户id 查询
         * @param id
         * @return
         */
        public User getUser(Long id){
            DetachedCriteria detachedCriteria = DetachedCriteria.forClass(User.class);
            detachedCriteria.add(Restrictions.eq("id",id));
            List<User> byCriteria = (List<User>) hibernateTemplate.findByCriteria(detachedCriteria);
            return byCriteria.size() > 0 ? byCriteria.get(0) : null;
        }
    
        /**
         * 更新
         * @param id
         * @param name
         * @return
         */
        public boolean update(Long id,String name){
            try {
                User user = new User();
                user.setId(id);
                user.setName(name);
                this.hibernateTemplate.update(user);
                return true;
            }catch (Exception e){
                e.printStackTrace();
            }
            return false;
    
        }
    
        public boolean delete(Long id){
            User user = hibernateTemplate.get(User.class, id);
            this.hibernateTemplate.delete(user);
            return true;
        }
    }
    
    
    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
  6. 测试

    package com.mszlu;
    
    
    import com.mszlu.config.SpringConfig;
    import com.mszlu.pojo.hibernate.User;
    import com.mszlu.service.UserHibernateService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class HibernateApp {
    
        public static void main(String[] args) {
            ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
            UserHibernateService userHibernateService = context.getBean(UserHibernateService.class);
    
            userHibernateService.save("testsave",50,"testsave@mszlu.com");
    
            User user = userHibernateService.getUser(1L);
            System.out.println(user);
    
            boolean update = userHibernateService.update(6L, "hhhhh");
            System.out.println("update:"+update);
            boolean delete = userHibernateService.delete(6L);
            System.out.println("delete:"+delete);
        }
    }
    
    
    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
  7. 使用hibernate坑比较多,建议必须熟练使用和了解hibernate才行,本人对hibernate不熟悉,此处只做演示,建议如果想学hibernate可以去看专业的教程

4. Spring整合JPA

Spring内置了JPA的集成,并支持选择Hibernate或EclipseLink作为实现。

JPA学习成本高,使用JPA需熟悉文档才行,此处只做演示

导包和hibernate一致

步骤:

  1. 定义DataSource

  2. 创建一个LocalContainerEntityManagerFactoryBean,并让它再自动创建一个EntityManagerFactory

    package com.mszlu.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.JpaVendorAdapter;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.persistence.EntityManagerFactory;
    import javax.sql.DataSource;
    import java.util.Properties;
    
    @Configuration
    @EnableTransactionManagement(proxyTargetClass = true)
    public class JPAConfig {
    
        @Bean
        LocalContainerEntityManagerFactoryBean createEntityManagerFactory(@Autowired DataSource dataSource) {
            LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
            // 设置DataSource:
            entityManagerFactoryBean.setDataSource(dataSource);
            // 扫描指定的package获取所有entity class:
            entityManagerFactoryBean.setPackagesToScan("com.mszlu.pojo.hibernate");
            // 指定JPA的提供商是Hibernate:
            JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
            entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
            // 设定特定提供商自己的配置:
            Properties props = new Properties();
            props.setProperty("hibernate.hbm2ddl.auto", "update");
            props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL57Dialect");
            props.setProperty("hibernate.show_sql", "true");
            entityManagerFactoryBean.setJpaProperties(props);
            return entityManagerFactoryBean;
        }
    
        @Bean
        PlatformTransactionManager transactionManager(@Autowired EntityManagerFactory entityManagerFactory) {
            return new JpaTransactionManager(entityManagerFactory);
        }
    }
    
    
    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
  3. Service 增删改查

    package com.mszlu.service;
    
    import com.mszlu.pojo.hibernate.User;
    import org.hibernate.criterion.DetachedCriteria;
    import org.hibernate.criterion.Restrictions;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.orm.hibernate5.HibernateTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import java.util.List;
    
    
    @Service
    @Transactional
    public class UserJPAService {
        //Spring遇到标注了@PersistenceContext的EntityManager会自动注入代理,该代理会在必要的时候自动打开EntityManager。
        // 换句话说,多线程引用的EntityManager虽然是同一个代理类,但该代理类内部针对不同线程会创建不同的EntityManager实例。
        @PersistenceContext
        EntityManager em;
    
        public void save(String name,Integer age,String email){
            User user = new User();
            user.setAge(age);
            user.setEmail(email);
            user.setName(name);
            em.persist(user);
            System.out.println("新增成功,user id: " + user.getId());
        }
    
        /**
         * 根据用户id 查询
         * @param id
         * @return
         */
        public User getUser(Long id){
            User user = this.em.find(User.class, id);
            return user;
        }
    
        /**
         * 更新
         * @param id
         * @param name
         * @return
         */
        public boolean update(Long id,String name){
            try {
                User user = this.getUser(id);
                user.setName(name);
                this.em.merge(user);
                return true;
            }catch (Exception e){
                e.printStackTrace();
            }
            return false;
    
        }
    
        public boolean delete(Long id){
            this.em.remove(em.getReference(User.class,id));
            return true;
        }
    }
    
    
    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
  4. 测试

    package com.mszlu;
    
    
    import com.mszlu.config.SpringConfig;
    import com.mszlu.pojo.hibernate.User;
    import com.mszlu.service.UserJPAService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class JPAApp {
    
        public static void main(String[] args) {
            ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
            UserJPAService userJPAService = context.getBean(UserJPAService.class);
    
            userJPAService.save("testsave",50,"testsave@mszlu.com");
    
            User user = userJPAService.getUser(1L);
            System.out.println(user);
    
            boolean update = userJPAService.update(8L, "hhhhh");
            System.out.println("update:"+update);
    //        boolean delete = userJPAService.delete(7L);
    //        System.out.println("delete:"+delete);
        }
    }
    
    
    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

5. Spring事务

以Mybatis来演示Spring事务的使用

事务的场景:

  1. 注册用户
  2. 增加积分
  3. 送注册大礼包

有任何一个操作 不成功 就任务不成功

CREATE TABLE `user_bonus`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `bonus` int(11) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
1
2
3
4
5
6
CREATE TABLE `user_gift`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `gift` tinyint(4) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
1
2
3
4
5
6

pojo:

package com.mszlu.pojo;

import lombok.Data;

@Data
public class UserBonus {

    private Integer id;

    private Long userId;

    private Integer bonus;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mszlu.pojo;

import lombok.Data;

@Data
public class UserGift {

    private Integer id;

    private Long userId;

    private Integer gift;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

service:

@Resource
    private UserBonusMapper userBonusMapper;
    @Resource
    private UserGiftMapper userGiftMapper;

    public void register(String name,Integer age,String email){
        User user = new User();
        user.setAge(age);
        user.setEmail(email);
        user.setName(name);
        this.userMapper.save(user);

        UserBonus userBonus = new UserBonus();
        userBonus.setBonus(5);
        userBonus.setUserId(user.getId());
        userBonusMapper.save(userBonus);

        int i = 10/0;
        UserGift userGift = new UserGift();
        userGift.setUserId(user.getId());
        //0 未发放 1 未领 2 已领
        userGift.setGift(1);
        userGiftMapper.save(userGift);

    }
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:

package com.mszlu.mapper;

import com.mszlu.pojo.UserGift;
import org.apache.ibatis.annotations.Insert;

public interface UserGiftMapper {
    @Insert("insert into user_gift (user_id,gift) values(##{userId},##{gift})")
    void save(UserGift userGift);
}

1
2
3
4
5
6
7
8
9
10
package com.mszlu.mapper;

import com.mszlu.pojo.UserBonus;
import org.apache.ibatis.annotations.Insert;

public interface UserBonusMapper {

    @Insert("insert into user_bonus (user_id,bonus) values(##{userId},##{bonus})")
    void save(UserBonus userBonus);
}

1
2
3
4
5
6
7
8
9
10
11

运行代码,调用注册方法,我们发现 报了异常 用户新增成功,积分添加成功,但是大礼包数据并没有,这时候我们得业务应该是如果 三个操作 有任意一个操作不成功,那么所有的操作都不能成功,需要回滚数据

5.1 ACID

  • 原子性(Atomicity)指事务是一个不可分割的整体,其中的操作要么全执行或全不执行
  • 一致性(Consistency)事务前后数据的完整性必须保持一致
  • 隔离性(Isolation)事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
  • 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

5.2 隔离级别

脏读:允许读取未提交的信息

  • 原因:Read uncommitted (RU)

解决方案: Read committed(表级读锁)RC

不可重复读:读取过程中单个数据发生了变化

  • 解决方案: Repeatable read (行级写锁) RR

幻读:读取过程中数据条目发生了变化

  • 解决方案: Serializable(表级写锁)

mysql默认隔离级别为RR,但是无幻读问题,使用了间隙锁+MVVC解决

5.3 Spring事务

5.3.1 Spring事务核心对象

  • Spring为业务层提供了整套的事务解决方案

    • PlatformTransactionManager 事务管理器
    • TransactionDefinition 定义事务
    • TransactionStatus 事务状态
5.3.1.1 PlatformTransactionManager

平台事务管理器实现类

  • DataSourceTransactionManager 适用于Spring JDBC或MyBatis
  • HibernateTransactionManager 适用于Hibernate3.0及以上版本
  • JpaTransactionManager 适用于JPA
  • JdoTransactionManager 适用于JDO
  • JtaTransactionManager 适用于JTA

此接口定义了事务的基本操作

  • 获取事务 :

    TransactionStatus getTransaction(TransactionDefinition definition)
    
    1
  • 提交事务 :

    void commit(TransactionStatus status) 
    
    1
  • 回滚事务 :

    void rollback(TransactionStatus status)
    
    1
5.3.1.2 TransactionDefinition

此接口定义了事务的基本信息

  • 获取事务定义名称

    String getName()
    
    1
  • 获取事务的读写属性

    boolean isReadOnly()
    
    1
  • 获取事务隔离级别

    int getIsolationLevel()
    
    1
  • 获事务超时时间

    int getTimeout()
    
    1
  • 获取事务传播行为特征

    int getPropagationBehavior()
    
    1
5.3.1.3 TransactionStatus

此接口定义了事务在执行过程中某个时间点上的状态信息及对应的状态操作

  • 获取事务是否处于新开启事务状态

    boolean isNewTransaction()
    
    1
  • 获取事务是否处于已完成状态

    boolean isCompleted()
    
    1
  • 获取事务是否处于回滚状态

    boolean isRollbackOnly()
    
    1
  • 刷新事务状态

    void flush()
    
    1
  • 获取事务是否具有回滚存储点

    boolean hasSavepoint()
    
    1
  • 设置事务处于回滚状态

    void setRollbackOnly()
    
    1

5.4 事务控制方式

  • 编程式
  • 声明式

5.4.1 编程式

	 //创建事务管理器
    DataSourceTransactionManager dstm = new DataSourceTransactionManager();
    //为事务管理器设置与数据层相同的数据源
    dstm.setDataSource(dataSource);
    //创建事务定义对象
    TransactionDefinition td = new DefaultTransactionDefinition();
    //创建事务状态对象,用于控制事务执行
    TransactionStatus ts = dstm.getTransaction(td);
    //异常 int i = 10/0 遇到异常回滚
    //提交事务
    dstm.commit(ts);
1
2
3
4
5
6
7
8
9
10
11

5.4.2 声明式

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>
1
2
3
4
5
5.4.2.1 XML
 <!--定义事务管理的通知类-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--定义控制的事务-->
        <tx:attributes>
            <tx:method
                    name="update*"
                    read-only="false"
                    timeout="-1"
                    isolation="DEFAULT"
                    no-rollback-for=""
                    rollback-for=""
                    propagation="REQUIRED"
            />
            <tx:method
                    name="save*"
                    read-only="false"
                    timeout="-1"
                    isolation="DEFAULT"
                    no-rollback-for=""
                    rollback-for=""
                    propagation="REQUIRED"
            />
        </tx:attributes>
    </tx:advice>

    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <aop:config >
        <aop:pointcut id="pt" expression="execution(public * com..service.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
    </aop:config>
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
5.4.2.2 注解

@Transactional

  • 名称:@Transactional

  • 类型:方法注解,类注解,接口注解

  • 位置:方法定义上方,类定义上方,接口定义上方

  • 作用:设置当前类/接口中所有方法或具体方法开启事务,并指定相关事务属性

  • 范例:

    @Transactional(
        readOnly = false,
        timeout = -1,
        isolation = Isolation.DEFAULT,
        rollbackFor = {ArithmeticException.class, IOException.class},
        noRollbackFor = {},
        propagation = Propagation.REQUIRES_NEW
    )
    
    1
    2
    3
    4
    5
    6
    7
    8

tx:annotation-driven

  • 名称:tx:annotation-driven

  • 类型:标签

  • 归属:beans标签

  • 作用:开启事务注解驱动,并指定对应的事务管理器

  • 范例:

    <tx:annotation-driven transaction-manager="txManager"/>
    
    1

@EnableTransactionManagement

  • 名称:@EnableTransactionManagement
  • 类型:类注解
  • 位置:Spring注解配置类上方
  • 作用:开启注解驱动,等同XML格式中的注解驱动
  • 范例:
@Configuration
@EnableTransactionManagement
public class SpringConfig {
}
1
2
3
4

5.5 传播行为

  • REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。

  • REQUIRES_NEW:它会开启一个新的事务。如果一个事务已经存在,则先将这个存在的事务挂起。

  • SUPPORTS:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。

  • NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务。

  • MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。

  • NEVER:总是非事务地执行,如果存在一个活动事务,则抛出异常。

  • NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。 这是一个嵌套事务