搭建SpringBoot准备
- javaweb基础
- idea使用基础
- maven使用基础
开始搭建SpringBoot项目
- 创建springboot
- 设置Group、Artifact、Packaging
- 选择web及SpringBoot版本
- 配置application.properites
SpringBoot默认情况下没有项目名和端口号需要我们在application.properites文件内配置项目和端口号
再加上mysql配置
server.servlet.path=/evaluate
server.port=8081
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql:///evaluate?characterEncoding=utf-8&useSSL=false
spring.jpa.show-sql=true
Spring Boot 有两种类型的配置文件,application 和 bootstrap 文件
Spring Boot会自动加载classpath目前下的这两个文件,文件格式为 properties 或 yml 格式
*.properties 文件是 key=value 的形式
*.yml 是 key: value 的形式
*.yml 加载的属性是有顺序的,但不支持 @PropertySource 注解来导入配置,一般推荐用yml文件,看下来更加形象
bootstrap 配置文件是系统级别的,用来加载外部配置,如配置中心的配置信息,也可以用来定义系统不会变化的属性.bootstatp 文件的加载先于application文件
application 配置文件是应用级别的,是当前应用的配置文件
- 编写实体类类
因为要使用jpa,所以实体类中属性命名方式要和数据中的表按照规则对应
jpa简介:
JPA是Java Persistence API的简称,中文名Java持久层API,是一种规范,我们可以使用hibernate来实现这规范。
JPQL查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。
如:from Student s where s.name = ?
语法和SQL差别不大。
String Data jpa是对hibernate实现的jpa规范进行了轻量级封装。
String Data Jpa识别实体类
@Entity 识别为实体类
@id 识别为主键
@GeneratedValue(strategy = GenerationType.IDENTITY) 识别为自增
规则如下:
1.命名相同
2.如果数据中有下划线,下划线后面字母大写
即:实体类:danYuan < ----- >数据库表字段:dan_yuan
3.如果不使用它的自动对应配置,可以直接配置
@Table(name = “表名”)
@Column(name=“字段名”)
@Entity
@Table(name = "danyuan")
public class Danyuan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="danyuan_id")
private Integer danyuanId;
@Column(name="name")
private String name;
@Column(name="dizhi")
private String dizhi;
@Column(name="code")
private String code;
@Column(name="parent_code")
private String parentCode;
//get、set方法省略
}
如果不匹配的话会报错
6. DanyuanRepository接口
需要继承JpaRepository(简单增删改查)
JpaRepository<实体类,主键类型>
public interface DanyuanRepository extends JpaRepository<Danyuan,Integer> {
List<Danyuan> findByParentCode(String code);
}
- service层
public interface DanyuanService {
List<Danyuan> findByParentCode(String code);
}
继承JPaSpecificationExecutor复杂操作
(这里不需要)
JPaSpecificationExecutor<实体类>
接口约定命名规则
@Service
public class DanyuanServiceImpl implements DanyuanService {
@Autowired
private DanyuanRepository danyuanRepository;
@Override
public List<Danyuan> findByParentCode(String code) {
return danyuanRepository.findByParentCode(code);
}
}
- Controller
@Controller
@RequestMapping("/danyuan")
public class DanyuanController {
@Autowired
private DanyuanService danyuanService;
@RequestMapping("/findByParentCode")
@ResponseBody
public List<Danyuan> findByParentCode(@RequestParam("parentCode") String parentCode){
return danyuanService.findByParentCode(parentCode);
}
}
- 已经简单实现获取数据库数据显示在页面中
10.效果展示
junit测试
@RunWith(SpringRunner.class)
@SpringBootTest(class= 启动类.class)
public class test01{
@Test
public void test(){
}
}
补充:
spring data jpa 中的dao层实现原理是通过jdk动态代理找到SimpleJapRepository动态代理对象,SimpleJapRepository实现了JapRepository接口,调用SimpleJapRepository中方法来实现我们写的dao实现类,其中SimpleJapRepository是使用jpa规范中EntityManager方法。jpa规范通过hibernate来实现jpa语法,hibernate通过封装的jdbc生成sql来在数据库中执行SQL语句。
找到一个图很好的写出来了,该图是描述的ssm项目,原理一致
spring data jpa 使用jpql语句(nativeQuery参数为false)
在自定义方法中加@Query注解
@Query(value="from Danyuan where title = ?1",nativeQuery=false)
public Danyuan findJpql(String title)
spring data jpa 使用sql语句(nativeQuery参数为true)
@Query(value="select * from danyuan where title = ?1",nativeQuery=true)
public Danyuan findJpql(String title)
自定义SQL高效更新(一般是全表更新,效率不如单字段更新)
@Modifying
@Transactional(readOnly = false) //@odifying需要写事务支持
@Query(value="UPDATE Danyuan SET name=:name WHERE id=:id”,nativeQuery=true)
public Danyuan updateName(@Param("name") String name,@Param("id") String id)
返回map集
在JPA 2.0 中我们可以使用entityManager.createNativeQuery()来执行原生的SQL语句。 但当我们查询结果没有对应实体类时,query.getResultList()返回的是一个List<Object[]>。也就是说每行的数据被作为一个对象数组返回。
JPA的API中并没有提供返回map集。其实很多JPA的底层实现都是支持返回Map对象的如:
Hibernate的.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
综上所述:
Query query = entityManager.createNativeQuery(sql);
query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List<Map<String, Object>> resutl = query.getResultList();
这里需要注意的是, 用Map肯定要比用Object数组来的效率低。所以你要看性能下降是否在可接受范围内。
JpaSpecificationExecutor使用 specifications实现类动态查询
源码
public interface JpaSpecificationExecutor<T> {
/**
* 查询单个对象
*/
T findOne(Specification<T> spec);
/**
* 查询列表
*/
List<T> findAll(Specification<T> spec);
/**
* 分页 pageable分页参数
*/
Page<T> findAll(Specification<T> spec, Pageable pageable);
/**
* 查询列表 ,sort排序 new Sort(Sort.Direction.DESC,"num");
*/
List<T> findAll(Specification<T> spec, Sort sort);
/**
* 统计
*/
long count(Specification<T> spec);
}
specification:查询条件
自定义specification实现类
Predicate toPredicate(Root root, CriteriaQuery<?> query, CriteriaBuilder cb);封装条件
root:查询的根对象(查询的任何属性都可以从根对象中获取)
CriteriaQuery:顶层查询对象,自定义查询方式(一般不用)
CriterriaBuilder: 查询的构造器,封装了很多的查询条件
如下:
匿名内部类使用。如果不是自己再封装,需要指定泛型对象。
Specification<T> sp = new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
try {
Path<Object> createTimePath = root.get("createTime");
// query.orderBy(cb.desc(createTimePath));
} catch (Exception e) {
System.out.println("实体不存在createTime属性");
}
if (StringUtils.isNotBlank(sortField)) {
query.orderBy(cb.asc(root.get(sortField)));
if (StringUtils.isNotBlank(order)) {
if (order.equals("desc")) {
query.orderBy(cb.desc(root.get(sortField)));
}
}
}
Predicate pred = fsp.toPredicate(root, query, cb);
try {
//获取属性
Path<String> namePath = root.get("name");
if (Util.isNotEmpty(name)) {
//构造查询条件可以cb.equal,cb.like等
pred = cb.and(pred, cb.like(namePath, "%"+name+"%"));//and与形式拼接多个查询条件
}
} catch (Exception e) {
System.out.println("实体不存在name属性");
}
return pred;
}
};
分页参数
pageable
//默认当前页
if(Util.isEmpty(pageNum) || pageNum == 0){
pageNum = 1;
}
//默认每页条数
if(Util.isEmpty(pageSize)){
pageSize = 10;
}
PageRequest pageable = new PageRequest(pageNum-1, pageSize,sort)
一对多关系
一方
//1、声明关系 对方字节码对象
//@OneToMany(targetEntity = People.class)
//放弃外键 对象导航查询加载方式fetch 中EAGER 立即加载,LAZY延迟加载
@OneToMany(mappedBy="department",cascade=Cascade.ALL,fetch=FetchType.eager)
//2、配置外键(中间表)name外键名,referenceColumName主表主键
//@JoinColumn(name ="department_id",referenceColumName="id")
private Set<People> peoples = new HashSet<>();
多方
//1、声明关系 对方字节码对象
@ManyToOne(targetEntity = Department.class)
//2、配置外键(中间表)name外键名,referenceColumName主表主键
@JoinColumn(name ="id",referenceColumName="department_id")
private Department department;
都在多的一方维护外键
一的一方维护的话多一条update执行效果,先两条insert,在一条update更新外键
级联
删除主表数据,会把外键字段设置为null,然后删除主表数据,如果配置了放弃维护关联关系的权利,则不能上传,如果还想删除,使用级联删除引用,如果没有从表数据,可以删除。
操作:
1、需要区分操作主体
2、需要在操作主体的实体类上。添加级联属性
3、cascade 配置级联
@OneToMany(mappedBy="department",cascade="CascadeType.ALL")
添加
Department department = new Department();
department.setName("2121");
People p = new People();
p.setName("5565");
department.getPoples.add(p);
departmentDao.save(department);
删除就直接删除一方就可以了
多对多关系
多方
//1、声明关系 对方字节码对象
@ManyToMany(targetEntity = Department.class)
//2、配置外键(中间表)name外键名,referenceColumName主表主键
@JoinTable(name ="department_role)",
//当前对象在中间表中的外键
joinColumns={@joinColumn((name ="id",referenceColumName="department_id")})
// 对方对象在中间表的外键
inverseColumns={@joinColumn((name ="id",referenceColumName="role_id")})
private Set<Department> departments = newHashSet<>();
另一个多方
//1、声明关系 对方字节码对象
@ManyToMany(mappedBy = "role")
private Set<Role> roles= newHashSet<>();
被动的一方放弃维护权
对象导航查询
就是通过配置了多表映射,通过get对象得到关系对象信息