Spring Boot+JPA

Spring Data JPA

一个持久层框架,根据实体类自动生成表 (注意库仍旧自己创建)、默认提供常用dao方法、按照命名规则自动生成dao方法、自定义dao方法等,极大提升开发效率。

springboot集成新框架环境往往很容易:引入依赖,编写配置、[启用]、代码编写。

引入依赖

jpa核心依赖:

 		<!-- jpa依赖-->
 		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

附属必须依赖:使用mysql要引入驱动依赖。

		<!-- 配置mysql驱动-->
		<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- 配置druid链接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

当然还有一些非必须依赖,如lombok,这里就不帖了。

添加配置

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/ssm?allowMultiQueries=true&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
      username: root
      password: root
  jpa:
    hibernate:
      # create 启动时删数据库中的表,然后创建,退出时不删除数据表
      # create-drop 启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错
      # update 如果启动时表格式不一致则更新表,原有数据保留
      # validate 项目启动表结构进行校验 如果不一致则报错
      ddl-auto: update  # 第一次建表create  后面用update,要不然每次重启都会新建表
    show-sql: true      #控制台显示sql语句

特别注意:ddl-auto的值在表没创建时,先改为create运行创建表,创建完成后,改为update重新启动即可(如果update出错,可以将这条配置注释掉即可)。

此时jpa的基础环境已经完成了,只剩下代码的编写。

代码编写

主要和mybatis不同的就是,不用自动创建表(实体类差别),dao不用自己写.

1.实体类

jpa会根据实体类的信息创建表,下面只是示范内容,详细注解不止这些。

@Data
@Entity						//声明实体类
@Table(name = "jpa_test")	//声明表名
public class User{
    @Id													   //主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)    //自增
    private Integer id;
    @Column(name = "name",length = 20)   //设置字段,不设置的字段名默认为属性名(驼峰转蛇型)
    private String name;
    private String password;
    private String sex;
}

注解的name属性值可能会有红色下划线:
1.提示Assign data sources:对项目没有影响,出现报错的原因是找不到对应的表,解决办法是让idea连接数据库,然后鼠标放于报错点,在弹窗设置数据源,如果数据库没有实体类对应表,将yml改为create运行一次即可。
2.其它错误:可能是注解包导错了,正确包为:javax.persistence。

2.Controller

常规的控制层

@RestController
public class JpaController {
    @Autowired
    private UserService userService;

    @PostMapping("/add")
    public Map<String,String> addUser(){
        User user=new User();
        user.setName("张三");
        user.setPassword("123456");
        user.setSex("男");
        userService.insertUser(user);
        Map<String,String> map=new HashMap<>();
        map.put("msg","操作成功");
        return map;
    }
}

3.Service与ServiceImpl

常规的Service层接口和ServiceImp实现。

业务层接口类

public interface UserService {
    //查询全部
    List<User> findUserList();
    //查询一条
    User findUserById(int id);
    //添加
    void insertUser(User user);
    //删除
    void deleteUser(int id);
    //修改
    void updateUser(User user);
}

业务层实现类:

查询一条数据时没有直接使用User而是使用Optional< User >,这是由于Dao层直接使用了默认的方法。
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public List<User> findUserList() {
        return userMapper.findAll();
    }

    @Override
    public User findUserById(int id) {
        //Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
        Optional<User> uo = userMapper.findById(id);
        return uo.orElse(null);
    }

    @Override
    public void insertUser(User user) {
        userMapper.save(user);
    }

    @Override
    public void deleteUser(int id) {
        userMapper.deleteById(id);
    }

    @Override
    public void updateUser(User user) {
        User emp=userMapper.findById(user.getId()).orElse(null);
        assert emp != null;
        BeanUtils.copyProperties(user,emp); //属性值拷贝
        userMapper.save(emp);
    }
}

修改和添加都是save方法,修改时对象id有值,添加时id无值。

4.Dao

继承接口JpaRepository,它默认的提供了一些常见dao方法。

@Repository
public interface UserMapper extends JpaRepository<User,Integer> {
	//约束1为实体类类型、约束2为主键类型
}

至此基础的crud就完成了,


附录:三种实现Dao功能方式

1.继承接口,使用默认接口+实现

接口作用
CrudRepository提供默认增删改查方法
PagingAndSortingRepositoryCRUD方法+分页、排序
JpaRepository针对关系型数据库优化

2.根据接口命名规则默认生成实现

默认提供了常见方法,但仍可以根据命名规则自动生成方法。
此表内容来源于官网:https://docs.spring.io/spring-data/jpa/docs/2.4.3/reference/html/#jpa.query-methods.query-creation

关键词示例JPQL片段
DistinctfindDistinctByLastnameAndFirstnameselect distinct …​ where x.lastname = ?1 and x.firstname = ?2
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is, EqualsfindByFirstname,findByFirstnameIs,findByFirstnameEquals… where x.firstname = ?1
BetweenfindByStartDateBetween… where x.startDate between ?1 and ?2
LessThanfindByAgeLessThan… where x.age < ?1
LessThanEqualfindByAgeLessThanEqual… where x.age <= ?1
GreaterThanfindByAgeGreaterThan… where x.age > ?1
GreaterThanEqualfindByAgeGreaterThanEqual… where x.age >= ?1
AfterfindByStartDateAfter… where x.startDate > ?1
BeforefindByStartDateBefore… where x.startDate < ?1
IsNull, NullfindByAge(Is)Null… where x.age is null
IsNotNull, NotNullfindByAge(Is)NotNull… where x.age not null
LikefindByFirstnameLike… where x.firstname like ?1
NotLikefindByFirstnameNotLike… where x.firstname not like ?1
StartingWithfindByFirstnameStartingWith… where x.firstname like ?1(参数附后%)
EndingWithfindByFirstnameEndingWith… where x.firstname like ?1(参数加前缀%)
ContainingfindByFirstnameContaining… where x.firstname like ?1(参数绑定在中%)
OrderByfindByAgeOrderByLastnameDesc… where x.age = ?1 order by x.lastname desc
NotfindByLastnameNot… where x.lastname <> ?1
InfindByAgeIn(Collection ages)… where x.age in ?1
NotInfindByAgeNotIn(Collection ages)… where x.age not in ?1
TruefindByActiveTrue()… where x.active = true
FalsefindByActiveFalse()… where x.active = false
IgnoreCasefindByFirstnameIgnoreCase… where UPPER(x.firstname) = UPPER(?1)

根据命名规则默认实现接口的操作如下:
在这里插入图片描述

3.自定义接口+实现(类似MyBatis)

使用注解方式 @Query

使用 @Query()注解来生成sql语句,注意此注解默认value属性值与myBatis有点不同,它使用的是JPQL。

如果想使value值为原生SQL,则添加属性:nativeQuery = true 即可。

表名映射:可以直接使用表对应类名,如果想用表名:#{#entityName}
参数映射:?n表示第n个参数、:参数名(参数可用@Param指定)

#{#entityName}:SPEL表达式,实体类使用了@Entity后,它的值为实体类名,如果@Entity的name属性有值,则它的值为该name值。
@Modifying:标记仅映射参数的方法。
@Transactional:开启事务,并将只读改为非只读。

@Repository
//约束1为实体类、约束2为主键
public interface UserMapper extends JpaRepository<User,Integer> {

    //添加:使用了原生sql
    @Transactional//开启事务为非只读
    @Modifying
    @Query(value = "insert into jpa_test(name,password,sex) values(:#{#user.name} ,:#{#user.password},:#{#user.sex}) ",nativeQuery = true)
    void addUser(@Param("user") User user);

    //删除
    @Transactional(timeout = 10)
    @Modifying
    @Query("delete from User where id=:id")
    void deleteUserById(@Param("id") Integer id);

    //修改
    @Transactional(timeout = 10)
    @Modifying
    @Query("update User u set u.name=:#{#user.name},u.password=:#{#user.password},u.sex=:#{#user.sex} where u.id=:#{#user.id}")
    void updateUser(@Param("user")User user);

    //查询一条
    @Query("select u from User u where u.id=?1 ")
    User findUserById(Integer id);

    //查询全部
    @Query("select u from User u")
    List<User> findAllUser();
}

对象属性的绑定:使用 @Param(映射名) 注解 + :#{#映射名.属性}

多表关联

JPA中一般只需要创建关联性即可,默认方法会自动关联查询。

1.一对一关联

两张表a、b,a的每条对应着b最多一条数据。
在这里插入图片描述

jpa实现如下:

/**
 * 表A
 */
@Entity
@Table(name = "a")
public class A{

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	private String name;
	
	@OneToOne(cascade = {CascadeType.ALL})//一对一关系,级联删除
	@JoinColumn(name="b",referencedColumnName = "id")//关联 b的id字段
	private B b;
}

/**
 * 表B
 */
@Entity
@Table(name = "b")
public class B{

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	
	private Long id;
	
	private String name;
}


上面是A级联B,即可以通过A查到B,如果想通过B查到A则需要为B添加级联属性。

2.一对多、多对一

一对多: 两张表A、B,A的一条记录对应B的多条记录,B每条只能对应1个A。
在这里插入图片描述
A对B的关系为一对多;B对 A的关系为多对一。

JPA实现:

球员与位置的关系,一个球员只有一个位置,一个位置可以有多个球员。

/**
 * 球员表
 */
@Entity//球员表
@Table(name = "sportmans")
public class SportMan implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    private String sportManName;
    
    @ManyToOne(cascade = {CascadeType.MERGE,CascadeType.PERSIST})
    @JoinColumn(name="duty")  //库中添加的外键字段
    private Duty duty;
}



/**
 * 位置表
 */
@Data
@Entity
@Table(name = "dutys")
public class Duty implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    private String dutyName;

    @JsonIgnore//不反向查询
    //级联保存、更新、删除,删除时会删除所有球员
    @OneToMany(mappedBy = "duty",cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    private List<SportMan> sportManList;


}

不使用@JsonIgnore注解时,查询球员,球员里关联出位置,位置反向关联球员,会无限递归查询,因此添加此注解,防止此字段被查出来时自动回查。

3.多对多

两张表A、B,一条A记录对应多条B,一条B记录对应多条A。
在这里插入图片描述

JPA实现:

/**
 * 表A
 */
@Entity
@Table(name = "a")
public class A{

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	private String name;
	
	@ManyToMany(cascade = {CascadeType.ALL})
	@JoinColumn(name="b",referencedColumnName = "id")//关联 b的id字段
	private B b;
}

/**
 * 表B
 */
@Entity
@Table(name = "b")
public class B{

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	
	private Long id;
	
	private String name;

	@ManyToMany(cascade = {CascadeType.ALL})
	@JoinColumn(name="a",referencedColumnName = "id")//关联 a的id字段
	private A a;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值