jpa应用

总结:

 1 表的起名尽量不要是关键字(order): 配置一对多等映射关系时,会报错

2 一些映射关系,都会进行多余的sql操作,很影响效率。

1 前置配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useTimezone=true&serverTimezone=GMT%2B8&allowMultiQueries=true
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver  #驱动
  jpa:
    hibernate:
      ddl-auto: update #自动更新
    show-sql: true  #日志中显示sql语句

 ddl-auto属性用于设置自动表定义

  • create 启动时删数据库中的表,然后创建,退出时不删除数据表
  • create-drop 启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错
  • update 如果启动时表格式不一致则更新表,原有数据保留
  • validate 项目启动表结构进行校验 如果不一致则报错
     <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
 <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

2属性的介绍

      以下属性都是围绕 @Entity 的实体下解释

@Entity(name="t_user") 1:该实体类就是数据库的表必须加。否则像数据库添加数据报错。2:不指定数据库表名,就会认为表名和实体类一样,数据库没有就会创建表(ddl_auto)
@Table

1:就是指定映射哪个表 

@Entity+@Table(name="t_user") 等效于@Entity(name="t_user")

@Basic1:隐藏字段,当我们实体类中属性不加任何注解,也会和数据库映射
@Transient1:实体类中属性和数据库无关连时,必须加上该注解。
@Column1:属性和数据库不一样时可以映射数据库字段
@Temporal(TemporalType.DATE)1:日期类型属性,和数据库精度对应。(DATE,TIME,TIMESTAMP)

        

3主键配置:

方式一:自增方式

   @Id
    @Column(name = "t_id")
    @GeneratedValue //主键自增
    private Long id;

方式二:通过表监控方式自增

 1:先要创建一个数据库表

CREATE TABLE `jpa_id_generators` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `pk_name` varchar(50) DEFAULT NULL,
  `pk_value` bigint DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

 @Id
 @Column(name = "t_id")
 /**
  * 根据表方式自增
  */
 @TableGenerator(name = "ID_GENERATOR",
         table = "jpa_id_generators",
         pkColumnName = "pk_name",
         pkColumnValue = "user_id",
         valueColumnName = "pk_value",
         allocationSize = 1) //增长跨度 每次添加数据主键+1
 @GeneratedValue(strategy = GenerationType.TABLE, generator = "ID_GENERATOR")
 private Long id;

详解: 用jpa_id_generators 这个表的 pk_name字段下 的 user_id的pk_value作为主键。没加一条数据,user_id的pk_value会+1  (配置的是+1)


import lombok.Data;
import lombok.ToString;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.util.List;

@Data
@ToString
@Entity
@Table(name = "t_user")
public class Tuser implements Serializable {
    //t_id 区分数据库和实体字段
    @Id
    @Column(name = "t_id")
    // @GeneratedValue 主键自增
    /**
     * 根据表方式自增
     */
    @TableGenerator(name = "ID_GENERATOR",
            table = "jpa_id_generators",
            pkColumnName = "pk_name",
            pkColumnValue = "user_id",
            valueColumnName = "pk_value",
            allocationSize = 10) //增长跨度 每次+1
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "ID_GENERATOR")
    private Long id;
    //t_name
    @Column(name = "t_name", length = 6)
    private String name;
    //t_age
    @Column(name = "t_age")
    private Integer age;
    //t_address
    @Column(name = "t_address")
    private String address;
   /* @Column(name = "create_time")
    @Basic
    */
    /**
     * 这个字段不加任何注解 也会保存到数据库中,也就是默认每一个属性都是有隐藏注解的@Basic
     * 如果是驼峰形式会自动转换 createTime-->create_time
     *
     * @Temporal(TemporalType.DATE)
     * @Temporal(TemporalType.TIME)
     * @Temporal(TemporalType.TIMESTAMP)
     */
    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;
    /**
     * 这个字段我们是和数据库没有任何关系的就用这个注解
     */
    @Transient
    private String aaa;
    /**
     * 订单数据库 一对多 它就会认为用户表主键和 订单表 user_id存在外键关系
     * 执行sql相当于 t_user a  left  join t_order b  on  a.t_id= b.user_id
     * 如果没有建立外键,则会在 t_order表建立外键
     */
    @JoinColumn(name = "user_id")
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private List<Torder> order;
}
@Data
@ToString
@Entity
@Table(name = "t_order")
public class Torder implements Serializable {
    @Id
    @Column(name = "id")
    /**
     * 根据表方式自增
     */
    @GeneratedValue
    private Integer id;
    @Column(name = "test_id")
    private Integer testId;
    @Column(name = "order_id")
    private Integer orderId;
    @Column(name = "user_id")
    private Integer userId;
}

/**
 * Tuser 对应数据库表
 * Long 主键类型
 */
public interface UserRepository extends JpaRepository<Tuser,Long> {
}

4映射关系

 总结: 1:软删除(不是真的删除 isdelete=0) 怎么映射出? 查询更新 都要加啊

             2:需要额外执行一些多余sql影响效率

 单向多对一,单向一对多,双向多对一,双向一对一,双向多对多

单向:就是只有一个配置了关系,(用户/订单 只有一个配置了关系)

双向:就是两个都配置关系(用户/订单 都配置了关系)

4.1 :一对多映射(一个用户多个订单)

  

 如下方式保存:

1: 如果没有建立外键,则会在t_order表中建立外键

2:t_order表中会自动填写对应的外键数据

    @Test
    void test2() {
        Tuser tuser = new Tuser();
        tuser.setName("jpa");
        Torder torder = new Torder();
        torder.setOrderId(000);
        List<Torder> torderList = new ArrayList<>();
        torderList.add(torder);
        tuser.setOrder(torderList);
        userRepository.save(tuser);
      
    }

 分开保存方式则不会生成给外键添值

    @Test
    void test2() {
        Tuser tuser = new Tuser();
        tuser.setName("jpa");
        Torder torder = new Torder();
        torder.setOrderId(000);
        userRepository.save(tuser);
        orderRepository.save(torder);
    }

 

删除方式:

 我只是删除了主表数据,但是从表中订单关联数据也没有了

cascade = CascadeType.ALL

        详解每个属性

        persist 保存时联级, merge 更新时联级, remove 删除时联级, refresh , detach

 userRepository.deleteById(4L);

4.2 :多对一映射(一个订单多个用户)

配置多对一时: 只能有一个 user_id 字段映射,如下图,只能存留一个。

/*    @Column(name = "user_id")
    private Integer userId;*/
    /**
     * 使用注解的形式配置多对一关系
     * 1.配置表关系
     * @ManyToOne : 配置多对一关系
     * targetEntity:对方的实体类字节码
     * 2.配置外键(中间表)
     * * 配置外键的过程,配置到了多的一方,就会在多的一方维护外键
     */
    @ManyToOne(targetEntity = Tuser.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "user_id")
    private Tuser tuser;

4.3 多对多

总结:多对多只配置一个映射关系,。另一个引用即可

 角色对用户:

/**
 * 角色
 */
@Data
@Entity
@Table(name = "t_role")
public class Trole implements Serializable {
    @Id
    @Column(name = "t_role_id", length = 20)
    @GeneratedValue
    private Long id;

    @Column(name = "t_role_name", length = 25)
    private String name;
    /**
     * @JoinTable name 中间表的表名
     *    joinColumns:配置当前类的信息
     *       name:中间表的列名
     *       referencedColumnName:对应本类的哪个字段映射
     *    inverseJoinColumns:配置另一个类的信息
     *        name:中间表的列名
     *        referencedColumnName:另一个类的哪个字段映射
     */
    @ManyToMany
    @JoinTable(name = "user_role",
            joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "t_role_id")},
            inverseJoinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "t_id")}
             )
    private List<Tuser> tuserList;
}

 

 就这俩字段是不是太少了?时间呢?是否删除呢?

配置了映射关系的一方先保存,

    @Test
    void test3() {
        List<Tuser> tuserList = new ArrayList<>();
        List<Trole> troleList = new ArrayList<>();
        Tuser tuser = new Tuser();
        tuser.setName("学生");
        Trole trole = new Trole();
        trole.setName("低级权限");
        tuserList.add(tuser);
        troleList.add(trole);
        trole.setTuserList(tuserList);
        tuser.setTroleList(troleList);
        userRepository.save(tuser);
        roleRepository.save(trole);
    }

5自定义sql

两种方式:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

/**
 * Tuser 对应数据库表
 * Long 主键类型
 */
public interface UserRepository extends JpaRepository<Tuser,Long> {
    /**
     * 方式一 写类名 和类中属性
      * @param username
     * @param id
     * @return
     */
    @Transactional
    @Modifying
    @Query("update Tuser a set a.name = ?1 where a.id = ?2")
    int updateTuser1(String username, Long id);

    /**
     * 方式二 直接写sql
     * nativeQuery true表示数据的sql   分页时就要用这种方式
     * @return
     */
    @Transactional
    @Modifying
    @Query(value="update t_user a set a.t_name = ?1 where a.t_id = ?2",nativeQuery=true)
    int updateTuser2(String username, Long id);

    /**
     * 方式三 直接写sql
     * nativeQuery true表示数据的sql   分页时就要用这种方式
     * 有的版本可能 写法不同  :#{#tuser.name}/#{#tuser.name} 
     * @return
     */
    @Transactional
    @Modifying
    @Query(value="update t_user  set t_name =:#{#tuser.name}  where t_id = :#{#tuser.id}",nativeQuery=true)
    int updateTuser3(@Param("tuser") Tuser tuser);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值