官术网_书友最值得收藏!

  • 深入實踐Spring Boot
  • 陳韶健
  • 2885字
  • 2019-01-04 03:26:52

2.1 使用MySQL

對于傳統關系型數據庫來說,Spring Boot使用JPA(Java Persistence API)資源庫來實現對數據庫的操作,使用MySQL也是如此。簡單地說,JPA就是為POJO(Plain Ordinary Java Object)提供持久化的標準規范,即將Java的普通對象通過對象關系映射(Object-Relational Mapping,ORM)持久化到數據庫中。

2.1.1 MySQL依賴配置

為了使用JPA和MySQL,首先在工程中引入它們的Maven依賴,如代碼清單2-1所示。其中,指定了在運行時調用MySQL的依賴。

代碼清單2-1 JPA和Mysql依賴配置


<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

2.1.2 實體建模

首先創建一些普通對象,用來與數據庫的表建立映射關系,接著演示如何使用JPA對數據庫進行增刪查改等存取操作。

假如現在有三個實體:部門、用戶和角色,并且它們具有一定的關系,即一個用戶只能隸屬于一個部門,一個用戶可以擁有多個角色。它們的關系模型如圖2-1所示。

圖2-1 MySQL實體-關系模型示例

Spring Boot的實體建模與使用Spring框架時的定義方法一樣,同樣比較方便的是使用了注解的方式來實現。

部門實體的建模如代碼清單2-2所示,其中注解@Table指定關聯的數據庫的表名,注解@Id定義一條記錄的唯一標識,并結合注解@GeneratedValue將其設置為自動生成。部門實體只有兩個字段:id和name。程序中省略了Getter和Setter方法的定義,這些方法可以使用IDEA的自動生成工具很方便地生成。

代碼清單2-2 部門實體建模


@Entity
@Table(name = "deparment")
public class Deparment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    public Deparment() {
    }
        ……
}

用戶實體包含三個字段:id、name和createdate,用戶實體建模如代碼清單2-3所示。其中注解@ManyToOne定義它與部門的多對一關系,并且在數據庫表中用字段did來表示部門的ID,注解@ManyToMany定義與角色實體的多對多關系,并且用中間表user_role來存儲它們各自的ID,以表示它們的對應關系。日期類型的數據必須使用注解@DateTimeFormat來進行格式化,以保證它在存取時能提供正確的格式,避免保存失敗。注解@JsonBackReference用來防止關系對象的遞歸訪問。

代碼清單2-3 用戶實體建模


@Entity
@Table(name = "user")
public class User implements java.io.Serializable{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createdate;
    @ManyToOne
    @JoinColumn(name = "did")
    @JsonBackReference
    private Department deparment;
    @ManyToMany(cascade = {}, fetch = FetchType.EAGER)
    @JoinTable(name = "user_role",
            joinColumns = {@JoinColumn(name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "roles_id")})
    private List<Role> roles;
    public User() {
    }……

角色實體建模比較簡單,只要按設計的要求,定義id和name字段即可,當然同樣必須保證id的唯一性并將其設定為自動生成。角色實體的建模如代碼清單2-4所示。

代碼清單2-4 角色實體建模


@Entity
@Table(name = "role")
public class Role implements java.io.Serializable{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    public Role() {
    }……

2.1.3 實體持久化

通過上面三個實體的定義,實現了使用Java的普通對象(POJO)與數據庫表建立映射關系(ORM),接下來使用JPA來實現持久化。

用戶實體使用JPA進行持久化的例子如代碼清單2-5所示。它是一個接口,并繼承于JPA資源庫JpaRepository接口,使用注解@Repository將這個接口也定義為一個資源庫,使它能被其他程序引用,并為其他程序提供存取數據庫的功能。

使用相同的方法,可以定義部門實體和角色實體的資源庫接口。接口同樣繼承于JpaRepository接口,只要注意使用的參數是各自的實體對象即可。

代碼清單2-5 用戶實體持久化


@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

這樣就實現存取數據庫的功能了?,F在可以對數據庫進行增刪查改、進行分頁查詢和指定排序的字段等操作。

或許你還有疑問,我們定義的實體資源庫接口并沒有聲明一個方法,也沒有對接口有任何實現的代碼,甚至連一條SQL查詢語句都沒有寫,這怎么可能?

是的,使用JPA就是可以這么簡單。我們來看看JpaRe-pository的繼承關系,你也許會明白一些。如圖2-2所示,JpaRepository繼承于PagingAndSortingRepository,它提供了分頁和排序功能,PagingAndSortingRepository繼承于Crud-Repository,它提供了簡單的增刪查改功能。

圖2-2 JpaRepository接口繼承關系

因為定義的接口繼承于JpaRepository,所以它傳遞性地繼承上面所有這些接口,并擁有這些接口的所有方法,這樣就不難理解為何它包含那么多功能了。這些接口提供的一些方法如下:


<S extends T> S save(S var1);
T findOne(ID var1);
long count();
void delete(ID var1);
void delete(T var1);
void deleteAll();
Page<T> findAll(Pageable var1);
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAll(Iterable<ID> var1);
void deleteAllInBatch();
T getOne(ID var1);
......

JPA還提供了一些自定義聲明方法的規則,例如,在接口中使用關鍵字findBy、readBy、getBy作為方法名的前綴,拼接實體類中的屬性字段(首個字母大寫),并可選擇拼接一些SQL查詢關鍵字來組合成一個查詢方法。例如,對于用戶實體,下列查詢關鍵字可以這樣使用:

·And,例如findByIdAndName(Long id,String name);

·Or,例如findByIdOrName(Long id,String name);

·Between,例如findByCreatedateBetween(Date start,Date end);

·LessThan,例如findByCreatedateLessThan(Date start);

·GreaterThan,例如findByCreatedateGreaterThan(Date start);

·IsNull,例如findByNameIsNull();

·IsNotNull,例如findByNameIsNotNull();

·NotNull,與IsNotNull等價;

·Like,例如findByNameLike(String name);

·NotLike,例如findByNameNotLike(String name);

·OrderBy,例如findByNameOrderByIdAsc(String name);

·Not,例如findByNameNot(String name);

·In,例如findByNameIn(Collection<String>nameList);

·NotIn,例如findByNameNotIn(Collection<String>nameList)。

又如下列對用戶實體類自定義的方法聲明,它們都是符合JPA規則的,這些方法也不用實現,JPA將會代理實現這些方法。


User findByNameLike(String name);
User readByName(String name);
List<User> getByCreatedateLessThan(Date star);

2.1.4 MySQL測試

現在,為了驗證上面設計的正確性,我們用一個實例來測試一下。

首先,增加一個使用JPA的配置類,如代碼清單2-6所示。其中@EnableTransac-tionManagement啟用了JPA的事務管理;@EnableJpaRepositories啟用了JPA資源庫并指定了上面定義的接口資源庫的位置;@EntityScan指定了定義實體的位置,它將導入我們定義的實體。注意,在測試時使用的JPA配置類可能與這個配置略有不同,這個配置的一些配置參數是從配置文件中讀取的,而測試時使用的配置類把一些配置參數都包含在類定義中了。

代碼清單2-6 JPA配置類


@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@EnableJpaRepositories(basePackages = "dbdemo.**.repository")
@EntityScan(basePackages = "dbdemo.**.entity")
public class JpaConfiguration {
    @Bean
    PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){
        return new PersistenceExceptionTranslationPostProcessor();
    }
}

其次,在MySQL數據庫服務器中創建一個數據庫test,然后配置一個可以訪問這個數據庫的用戶及其密碼。數據庫的表結構可以不用創建,在程序運行時將會按照實體的定義自動創建。如果還沒有創建一個具有完全權限訪問數據庫test的用戶,可以在連接MySQL服務器的查詢窗口中執行下面指令,這個指令假設你將在本地中訪問數據庫。


grant all privileges on test.* to 'root'@'localhost' identified by '12345678';

然后,在Spring Boot的配置文件application.yml中使用如代碼清單2-7所示的配置,用來設置數據源和JPA的工作模式。

代碼清單2-7 數據源和JPA配置


spring:
    datasource:
        url: jdbc:mysql:// localhost:3306/test?characterEncoding=utf8
        username: root
        password: 12345678
    jpa:
        database: MYSQL
        show-sql: true
    #Hibernate ddl auto (validate|create|create-drop|update)
        hibernate:
            ddl-auto: update
            naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
        properties:
            hibernate:
                dialect: org.hibernate.dialect.MySQL5Dialect

配置中將ddl-atuo設置為update,就是使用Hibernate來自動更新表結構的,即如果數據表不存在則創建,或者如果修改了表結構,在程序啟動時則執行表結構的同步更新。

最后,編寫一個測試程序,如代碼清單2-8所示。測試程序首先初始化數據庫,創建一個部門,命名為“開發部”,創建一個角色,命名為admin,創建一個用戶,命名為user,同時將它的所屬部門設定為上面創建的部門,并將現有的所有角色都分配給這個用戶。然后使用分頁的方式查詢所有用戶的列表,并從查到的用戶列表中,打印出用戶的名稱、部門的名稱和第一個角色的名稱等信息。

代碼清單2-8 MySQL測試程序


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {JpaConfiguration.class})
public class MysqlTest {
    private static Logger logger = LoggerFactory.getLogger(MysqlTest.class);
    @Autowired
    UserRepository userRepository;
    @Autowired
    DepartmentRepository departmentRepository;
    @Autowired
    RoleRepository roleRepository;
    @Before
    public void initData(){
        userRepository.deleteAll();
        roleRepository.deleteAll();
        departmentRepository.deleteAll();
        Department department = new Department();
        department.setName("開發部");
        departmentRepository.save(department);
        Assert.notNull(department.getId());
        Role role = new Role();
        role.setName("admin");
        roleRepository.save(role);
        Assert.notNull(role.getId());
        User user = new User();
        user.setName("user");
        user.setCreatedate(new Date());
        user.setDeparment(department);
        List<Role> roles = roleRepository.findAll();
        Assert.notNull(roles);
        user.setRoles(roles);
        userRepository.save(user);
        Assert.notNull(user.getId());
    }
    @Test
    public void findPage(){
        Pageable pageable = new PageRequest(0, 10, new Sort(Sort.Direction.ASC,
"id"));
        Page<User> page = userRepository.findAll(pageable);
        Assert.notNull(page);
        for(User user : page.getContent()) {
            logger.info("====user==== user name:{}, department name:{}, role
name:{}",
                user.getName(), user.getDeparment().getName(), user.getRoles().
get(0).getName());
        }
    }
}

好了,現在可以使用JUnit來運行這個測試程序了,在IDEA的Run/Debug Configuration配置中增加一個JUint配置項,模塊選擇mysql,工作目錄選擇模塊所在的根目錄,程序選擇dbdemo.mysql.test.MysqlTest,并將配置項目名稱保存為mysqltest,如圖2-3所示。

用Debug方式運行測試配置項目mysqltest,可以在控制臺中看到執行的過程和結果。如果狀態欄中顯示為綠色,并且提示“All Tests passed”,則表示測試全部通過。在控制臺中也可以查到下列打印信息:


dbdemo.mysql.test.MysqlTest - ====user==== user name:user, department name:開發部, role name:admin

這時如果在MySQL服務器中查看數據庫test,不但可以看到表結構都已經創建了,還可以看到上面測試生成的一些數據。

這是不是很激動人心?在Spring Boot使用數據庫,就是可以如此簡單和有趣。到目前為止,我們不僅沒有寫過一條查詢語句,也沒有實現一個訪問數據庫的方法,但是已經能對數據庫執行所有的操作,包括一般的增刪查改和分頁查詢。

圖2-3 JUint測試配置

主站蜘蛛池模板: 易门县| 虞城县| 乐昌市| 西藏| 大邑县| 浦东新区| 广西| 天津市| 习水县| 陆河县| 湖州市| 茂名市| 桂东县| 瓦房店市| 青铜峡市| 基隆市| 加查县| 吉安县| 德江县| 南通市| 芜湖县| 商南县| 神池县| 马关县| 高清| 峨边| 云浮市| 鱼台县| 防城港市| 丹棱县| 南靖县| 北安市| 沭阳县| 大同县| 萨迦县| 阜新市| 宝山区| 双城市| 肥西县| 大竹县| 巴中市|