导入mysql驱动,mybatis,lombok
在 application.properties 中配置数据源信息
#数据源1
spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.one.jdbc-url=jdbc:mysql://localhost:3306/boot1?characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.one.username=root
spring.datasource.one.password=034312
#需要注意的是,springboot2.0以上配置双数据源,配置文件中不能写url,而是要改成jdbc-url,否则会出错。
#数据源2
spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.two.jdbc-url=jdbc:mysql://localhost:3306/boot2?characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.two.username=root
spring.datasource.two.password=034312
创建DataSourceConfig配置类
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.one")
DataSource dsOne() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.two")
DataSource dsTwo() {
return DataSourceBuilder.create().build();
}
}
创建MybatisConfig***配置类
以MybatisConfiOne为例
@Configuration
@MapperScan(basePackages = "com.zhj.mapper1",sqlSessionTemplateRef = "sqlSessionTemplate1")
public class MyBatisConfigOne {
@Resource(name = "dsOne")
DataSource dsOne;
@Bean
SqlSessionFactory sqlSessionFactory1() {
SqlSessionFactory sessionFactory = null;
try {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dsOne);
bean.setTypeAliasesPackage("com.zhj.domain");
sessionFactory = bean.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return sessionFactory;
}
@Bean
SqlSessionTemplate sqlSessionTemplate1() {
return new SqlSessionTemplate(sqlSessionFactory1());
}
}
然后 后面正常创建 对应的 mapper ,service,controller
注意: pom文件配置
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
resource>
resources>
ORM即Object-Relational Mapping,他的作用是关系型数据库和对象之间的映射。这样,我们在具体操作数据库的时候,就不需要和复杂的SQL语句打交道,只要像平常操作对象操作他就可以了
在ORM出现之前,我们使用jdbc来操作数据库,但jdbc没有封装,对于大项目来说,很难实现MVC的概念,所以人们就开发了 ORM框架来解决这些问题。比如Hibernate,Mybatis,不过 Hibernate是完全的ORM框架,mybatis是半ORM框架,因为它需要手动建表和自己写sql
ORM的优点:提高了开发效率。由于ORM可以自动对Entity对象与数据库中的table进行字段与属性的映射,能够像操作对象一样 从数据库中获取数据
ORM的缺点:ORM的缺点是牺牲程序的执行效率,因为是自动生成的sql,所以实现复杂查询比较麻烦
ORM框架很多,大家各自搞自己的,为了统一规范,就出了JPA
JPA全称Java Persistence API ,可以通过注解 或者 XML描述【对象-关系表】之间的映射关系,并将对象持久化到数据库中
JPA为我们提供了:
(1)ORM映射元数据:JPA 支持XML 和 注解两种元数据形式,元数据描述对象与对象之间的映射关系,框架据此将实体对象持久化到数据库表中
如:@Entity,@Table,@Column,@Transient等注解
(2)API:用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来
如:entityMananger.merge(T t)
(3)JPQL查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合
但是:JPA仅仅是一种规范,也就是说JPA仅仅定义了一些接口,而接口需要实现才能工作,所以底层需要某种实现
而Hibernate就是实现了JPA接口的ORM框架
实现jpa中的接口需要写大量的代码,包括简单的增删改查,那可不可以有框架将这些写好呢,于是Spring Data Jpa 就出现了
Spring Data Jpa是spring提供一套简化JPA开发的框架,不仅有接口,也有实现类,只要按照约定好的【命名规则】写dao接口,就可以在不写接口实现的情况下,实现对数据库的访问和操作,同时提供了很多处理CRUD之外的功能,如分页 排序 复杂查询;
Spring Data JPA可以理解为JPA规范的再次封装抽象,底层还是使用了Hibernate的JPA技术实现
关于在开发中到底应该是使用JPA 还是使用 Mybatis 争论不休,总体来说,国外用JPA的多,国内用MyBatis的多
Spiring Data JPA是面向对象的思想,一个对象就是一个类,强化的是您对这个表的控制。spring data jpa实现了jpa 功能,即 可以实现pojo 转化为关系型数据库记录的功能,通俗来讲 可以不写任何建表sql 语句了。jpa 是spring data jpa功能的一个子集
MyBatis则是面向sql,您的结果完全来源于sql,而对象这个东西只是用来接受sql带来的结果集。您的一切操作都是围绕sql,包括动态根据条件约定sql语句等。mybatis并不是注意对象的概念。只要能接受数据就好
各自优缺点:
面向sql就更利于优化,因为sql可以优化的点太多的了。对于并发用户多,主求性能的,mybatis 更有优势。
面向对象就更利于移植,可维护性,因为数据对象不依赖数据源。比如mysql换成oracle,jpa更方便。
用哪个 最终取决于老板
Spring boot 中使用JPA 实际上是Spring Data JPA ,在 Spring Data 中,只要您的方法名符合规范,他就知道您想干什么,不许要自己再去写SQL。
创建工程
创建工程,添加Web,Jpa,MySql驱动,lombok依赖
添加druid 连接池依赖
springboot默认的连接是hikari
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.6version>
dependency>
配置连接信息 和 jpa 信息(application.properties)
# 配置数据库基本信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/boot3?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=034312
# 更换数据源类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 配置JPA
spring.jpa.database=mysql
# 是否在控制台打印SQL
spring.jpa.show-sql=true
# 每次启动项目,数据库初始化策略
#spring.jpa.hibernate.ddl-auto=create # 每次运行该程序没有表格会新建表,表内数据会清空
#spring.jpa.hibernate.ddl-auto=create-drop # 每次程序结束是会会清空表
#spring.jpa.hibernate.ddl-auto=update # 每次运行程序,没有表格会新建,表内有数据不会清空,只会更新
#spring.jpa.hibernate.ddl-auto=validate # 运行程序会校验数据库 与 对象 中字段类型是否相同,不同会报错
spring.jpa.hibernate.ddl-auto=update
# 指定默认存储引擎为InnoDB,默认情况下自动创建表时会使用 MyISAM 作为表引擎
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
创建domain
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "pwd")
private String pwd;
}
创建mapper
public interface UserMapper extends JpaRepository<User,Integer> {
}
在service 直接进行属性注入即可
Repository接口
提供方法名称命名查询方式
提供了基于@Query注解查询与 更新
CrudRepository接口
CrudRepository接口继承了Repository接口
CrudRepository提供了基本的增删改查,不需要我们自定义
PagingAndSortingRepository接口
该接口继承了CrudRepository接口
该该接口提供了分页与排序操作,也就是该接口不用自己定义增删改查方法和分页排序方法
JpaRepository接口
该接口继承了PagingAndSortingRepository
对继承的父接口中的方法的返回值进行适配,也就是该接口不用自己定义增删改查和分页排序方法,并且让分页查询更加简单
JpaSpecificationExcutor接口
该接口主要是提供了多条件查询的支持,并且可以在查询中添加排序和分页。注意JPASpecificationExcutor是单独存在的。不继承上述接口
@Autowired
private Mapper mapper;
查询所有:mapper.findAll()
查询单个:mapper.findById(id),get()
添加: mapper.save(domain)
修改用户: mapper.getOne(id) mapper.save(domain)
删除用户:mapper.delete()
分页查询
public List<User> selectUserByPage(){
PageRequest pageRequest = PageRequest.of(0, 3);
Page<User> page = userMapper.findAll(pageRequest);
List<User> userList = page.getContent();
return userList;
}
降序查询
public List<User> selectUserBySort(){
PageRequest pageRequest = PageRequest.of(0, 3, Sort.Direction.DESC,"id");
Page<User> page = userMapper.findAll(pageRequest);
List<User> userList = page.getContent();
return userList;
}
关键字 | 示例 | JPQL 表达式 |
---|---|---|
And |
findByLastnameAndFirstname |
… where x.lastname = ?1 and x.firstname = ?2 |
Or |
findByLastnameOrFirstname |
… where x.lastname = ?1 or x.firstname = ?2 |
Is , Equals |
findByFirstname , findByFirstnameIs , findByFirstnameEquals |
… where x.firstname = ?1 |
Between |
findByStartDateBetween |
… where x.startDate between ?1 and ?2 |
LessThan |
findByAgeLessThan |
… where x.age < ?1 |
LessThanEqual |
findByAgeLessThanEqual |
… where x.age <= ?1 |
GreaterThan |
findByAgeGreaterThan |
… where x.age > ?1 |
GreaterThanEqual |
findByAgeGreaterThanEqual |
… where x.age >= ?1 |
After |
findByStartDateAfter |
… where x.startDate > ?1 |
Before |
findByStartDateBefore |
… where x.startDate < ?1 |
IsNull , Null |
findByAge(Is)Null |
… where x.age is null |
IsNotNull , NotNull |
findByAge(Is)NotNull |
… where x.age not null |
Like |
findByFirstnameLike |
… where x.firstname like ?1 |
NotLike |
findByFirstnameNotLike |
… where x.firstname not like ?1 |
StartingWith |
findByFirstnameStartingWith |
… where x.firstname like ?1 (parameter bound with appended % ) |
EndingWith |
findByFirstnameEndingWith |
… where x.firstname like ?1 (parameter bound with prepended % ) |
Containing |
findByFirstnameContaining |
… where x.firstname like ?1 (parameter bound wrapped in % ) |
OrderBy |
findByAgeOrderByLastnameDesc |
… where x.age = ?1 order by x.lastname desc |
Not |
findByLastnameNot |
… where x.lastname <> ?1 |
In |
findByAgeIn(Collection ages) |
… where x.age in ?1 |
NotIn |
findByAgeNotIn(Collection ages) |
… where x.age not in ?1 |
True |
findByActiveTrue() |
… where x.active = true |
False |
findByActiveFalse() |
… where x.active = false |
IgnoreCase |
findByFirstnameIgnoreCase |
… where UPPER(x.firstame) = UPPER(?1) |
例如:
public interface UserMapper extends JpaRepository<User,Integer> {
List<User> findByIdLessThan(Integer id);
List<User> findByNameLike(String keyword);
}
public interface UserMapper extends JpaRepository<User,Integer> {
List<User> findByIdLessThan(Integer id);
List<User> findByNameLike(String keyword);
// 查询 id 最大的 user
@Query(value = "select * from user where id = (select max(id) from user)",nativeQuery = true)
User findUserByMaxId();
// 自定义模糊查讯
@Query(value = "select * from user where name like %:name%",nativeQuery = true)
List<User> findUserNameByLike(@Param("name")String name);
}
一对一
在实体类 配置 关联字段,其他不变
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer sid;
@Column(name = "name")
private String name;
@Column(name = "age")
private Integer age;
@OneToOne
// name 是本表字段 referencedColumnName 外表关联字段
@JoinColumn(name = "gid",referencedColumnName = "gid")
private Glass glass;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "glass")
public class Glass {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer gid;
@Column(name = "gname")
private String gname;
}
一对多
在 多端进行维护 如 一个班级有多个学生
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "glass")
public class Glass {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer gid;
@Column(name = "gname")
private String glassName;
@OneToMany(mappedBy = "glass") // 设置 一对多 mapperBy 把自己交由 他表 进行维护
private List<Student> studentList;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(exclude = "glass") // jpa 使用lombok是需要 排除关联表属性
@Entity(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer sid;
@Column(name = "name")
private String name;
@Column(name = "age")
private Integer age;
@ManyToOne // 设置 多对一
@JoinColumn(name = "gid") // 外键 关联字段
@JsonBackReference // 避免 json回调关联
private Glass glass;
}
多对多
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(exclude = "glass") // jpa 使用lombok是需要 排除关联表属性
@Entity(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer sid;
@Column(name = "name")
private String name;
@Column(name = "age")
private Integer age;
@ManyToMany
// name : 中间表 joinColumns: 主表字段 inverseJoinColumns : 副表字段
@JoinTable(name = "student_teacher",joinColumns = @JoinColumn(name = "sid"),inverseJoinColumns = @JoinColumn(name ="tid"))
private List<Teacher> teacherList;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "teacher")
public class Teacher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer tid;
@Column(name = "tname")
private String tname;
@Column(name = "tcourse")
private String tcourse;
}
一对一 和 多对多 只需要设置 一张表 ,而一对多 是双向关联的关系 需要设置 两张表(多端为 维护端)
作用 : 封装 where 以后的查询方法 例如:
public interface StudentMapper extends JpaRepository<Student,Integer>, JpaSpecificationExecutor<Student> {
}
@RequestMapping("select")
public List<Student> select(){
Specification specification = new Specification<Student>() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
ArrayList<Predicate> predicateArrayList = new ArrayList<>();
predicateArrayList.add(criteriaBuilder.equal(root.get("name"),"周弘杰"));
predicateArrayList.add(criteriaBuilder.equal(root.get("age"),14));
Predicate[] predicates = new Predicate[predicateArrayList.size()];
return criteriaBuilder.and(predicateArrayList.toArray(predicates)); // 使用 and 查询 并返回
}
};
return studentMapper.findAll(specification);
};
JPQL 全称java Persistence Query Language. 中文意思是 java 持久化查询语言
是一种可移植性查询语言,旨在以面向对象的表达式,将SQL语法和简单查询语义绑定在一起,使用这种语法编写的查询是 可移植性的,可以被编译所有主流数据库库服务器上的SQL
其特征 与 原声的SQL 语句类似,并且完全面向对象,通过类名与属性访问,而不是 表名和表的属性
查询用的SELECT 语法如下:
比如 : select u from user u where u.userName :userName
User : 实体类名称 默认是 domain中的实体类名,如果使用了 注解@Entity(name=“”),则为name
u:别名
u.userName:实体对象的属性
:userName:是传递的参数
JPQL 语言不支持 增加操作,即 只有 delete,update ,select
public interface StudentMapper1 extends JpaRepository<Student,Integer>, JpaSpecificationExecutor<Student> {
@Query("select s from student s where s.name=:name")
List<Student> selectStudentByName(@Param("name") String name);
}
修改与删除
@Modifying // 默认为查询 设置为修改
@Transactional // 开启事务
@Query("update student s set s.name=:name where s.sid =:sid")
Integer updateStudentById(@Param("name") String name,@Param("sid") Integer sid);
@Modifying
@Transactional
@Query("delete from student s where s.sid=:sid ")
Integer deleteStudentById(@Param("sid") Integer sid);
创建工程,添加Web,Jpa,MySql驱动,lombok依赖 ,druid
在 applicaiton.properties 中配置 数据源 与 jpa 相关配置
# 数据库的基本配置
spring.datasource.one.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.one.username=root
spring.datasource.one.password=034312
#注意多数据源要用jdbc-url
spring.datasource.one.jdbc-url=jdbc:mysql://localhost:3306/boot1? characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.two.username=root
spring.datasource.two.password=034312
spring.datasource.two.jdbc-url=jdbc:mysql://localhost:3306/boot2?characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
# JPA配置
spring.jpa.properties.database=mysql
spring.jpa.properties.show-sql=true
spring.jpa.properties.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
配置DataSourceConfig:创建config 包,在包下 创建 DataSourceConfig
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.one")
//这里添加@Primary注解,一定不能少,否则在项目启动时会出错,@Primary 表示当某一个类存在多个实例时,优先使用哪个实例
@Primary
DataSource dsOne() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.two")
DataSource dsTwo() {
return DataSourceBuilder.create().build();
}
}
在 config 下创建 JpaConfigOne 与 JpaConfigTwo
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.zhj.mapper1",
entityManagerFactoryRef = "entityManagerFactoryBeanOne",
transactionManagerRef = "platformTransactionManagerOne")
/*
basePackages 用来指定 dao 所在的位置。
entityManagerFactoryRef 用来指定实体类管理工厂 Bean 的名称
transactionManagerRef 用来指定事务管理器的引用名称,
默认的 Bean 名称为方法名
*/
public class JpaConfigOne {
@Resource(name = "dsOne")
DataSource dsOne;
@Autowired
JpaProperties jpaProperties;
@Bean
@Primary
//该 Bean 用来提供 EntityManager 实例
LocalContainerEntityManagerFactoryBean entityManagerFactoryBeanOne(
EntityManagerFactoryBuilder builder) {
return builder.dataSource(dsOne) //配置数据源
.properties(jpaProperties.getProperties())//设置 JPA 相关配置
.packages("com.zhj.domain")//设置实体类所在的位置
.persistenceUnit("pu1")//配置持久化单元名。若项目中只有一个 EntityManagerFactory,则 persistenceUnit 可以省略掉,若有多个,则必须明确指定持久化单元名。
.build();
}
//创建一个事务管理器。JpaTransactionManager 提供对单个 EntityManagerFactory 的事务支持,专门用于解决 JPA 中的事务管理
@Bean
PlatformTransactionManager platformTransactionManagerOne(
EntityManagerFactoryBuilder builder) {
LocalContainerEntityManagerFactoryBean factoryBeanOne
= entityManagerFactoryBeanOne(builder);
return new JpaTransactionManager(factoryBeanOne.getObject());
}
}
类似的创建 JpaConfigTwo,注意 去掉 @Primary
然后 建立相关的mapper 包 ,正常创建 mapper 类即可