SpringDataJPA

前言

ORM思想

主要目的:操作实体类就相当于操作数据库表
建立两个映射关系
	实体类和表的映射关系
	实体类中属性和表中字段的映射关系
不再重点关注 sql 语句
实现了 orm 思想的框架有 mybatis, hibernate

什么是 hibernate ?

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它
将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate 可以自动生成SQL语
旬,自动执行,使得Java程序员可以随心所欲的使用对象編程思维来操纵数据库。

JPA

JPA的全称是Java Persistence API,即Java持久化API,是SUN公司推出的一套基于ORM
的规范,内部是由一系列的接口和抽象类构成。
JPA通过JDK 5.0注解描述对象-->关系表的映射关系,并将运行期的实体对象持久化到数据库中。

优势:
	JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以
看成是Hibernate HQL的等价物。JPA定义了独特的JPQL (Java Persistence query
Language),JPQL 是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而
不是关系数据库的表,而且能够支持批量更新和修改、JOIN、 GROUP BY、HAVING 等通常只有
SQL才能够提供的高级查询特性,甚至还能够支持子查询。

在这里插入图片描述

hiberate 入门

<dependencies>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.12</version>
		<scope>test</scope>
	</dependency>

	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-entitymanager</artifactId>
		<version>5.3.7.Final</version>
	</dependency>

	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-c3p0</artifactId>
		<version>5.3.7.Final</version>
	</dependency>

	<dependency>
		<groupId>log4j</groupId>
		<artifactId>log4j</artifactId>
		<version>1.2.17</version>
	</dependency>

	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.47</version>
	</dependency>

</dependencies>
jpa 核心配置文件
src/META-INF/persistence.xml
<?xml version="1.0" encoding="utf-8" ?>
<!--导入schema约束,此约束来源:复制hibernate-core:5.2.16.Final包下的/org/hibernate/jpa/persistence_2_0.xsd文件中的这一段出来即可。 -->
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
	version="2.0">
	<!--配置持久化单元(可以配置多个,名称不能重复) 
		name:用于指定持久化单元的名称 
		transcation-type:指定事务的类型。 
		JTA:Java 
		Transcation API RESOURCE_LOCAL:指的是本地代码事务 -->
	<persistence-unit name="myPersistenceUnit"
		transaction-type="RESOURCE_LOCAL">
		<!--JPA规范提供商,可以不写 -->
		<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
		<!--指定Jpa注解的实体类型位置,可以不写 
		<class>com.demo.domain.Customer</class> 
		连接相关的一些配置,都是用hibernate的。 -->
		<properties>
			<!--第一部分,连接数据库信息 -->
			<property name="hibernate.connection.driver_class"
				value="com.mysql.jdbc.Driver"></property>
			<property name="hibernate.connection.url"
				value="jdbc:mysql://127.0.0.1:3306/data?characterEncoding=utf8"></property>
			<property name="hibernate.connection.username" value="root"></property>
			<property name="hibernate.connection.password" value="123"></property>
			<!--说明:数据库的方言,用于存放不同数据库之间的SQL语句差异。 -->
			<property name="hibernate.dialect"
				value="org.hibernate.dialect.MySQL57Dialect"></property>

			<!--第二部分,hibernate的可选配置 -->
			<!--是否显示hiberante的生成的SQL语句 -->
			<property name="hibernate.show_sql" value="true"></property>
			<!--是否使用格式化输出SQL语句到控制台 -->
			<property name="hibernate.format_sql" value="false"></property>
			<!--采用何种方式生成DDL语句,
				create: 程序运行时先删除表,在创建表
				update 程序运行时创建表,如果有表则不创建
				不一致,则更新数据库。 -->
			<property name="hibernate.hbm2ddl.auto" value="update"></property>
			<!--连接池的配置,这里使用的是c3p0连接池,常用的还有阿里的 -->
			<property name="hibernate.connection.provider_class"
				value="org.hibernate.c3p0.internal.C3P0ConnectionProvider"></property>
		</properties>
	</persistence-unit>
</persistence>

User.java

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity // 声明实体类
@Table(name = "user")
public class User {

	@Id // 主键自增
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	@Column(name = "username")
	private String username;
	private String password;
	@Column(name = "create_time")
	private Date createTime;
}

main.java

public class AppTest {
	
	public static void main(String[] args) {
		// 读取配置文件,创建实体管理器工厂
		EntityManagerFactory factory = Persistence.createEntityManagerFactory("myPersistenceUnit");
		// 获取实体管理器
		EntityManager em = factory.createEntityManager();
		// 获取事务对象
		EntityTransaction tx = em.getTransaction();
		// 开启事务
		tx.begin();
		// 保存一个用户到数据库
		User user = new User();
		user.setUsername("bingbing");
		user.setPassword("456");
		// 保存
		em.persist(user);
		// 提交事务
		tx.commit();
		// 释放资源
		em.close();
		factory.close();
	}

}
EntityManagerFactory :获取EntityManager对象
方法: createEntityManager 内部维护的很多的内容
	内部维护了数据库信息,
	维护了缓存信息
	维护了所有的实体管理器对象
	再创建EntityManagerFactory的过程中会根据配置创建数据库表
	
当EntityManagerFactory的创建过程比较浪费资源
	特点:线程安全的对象
	多个线程访间同一个EntityManagerFactory不会有线程安全间题
	
*如何解决EntityManagerFactory的创建过程浪费资源(耗时)的问题?
	思路:创建一个公共的EntityManagerFactory的对象
	*静态代码块的形式创建EntityManagerractory
	
创建事务对象,开启事务
EntityManagex对象:实体类管理器
	beginTransaction :创建事务对象
	presist :保存
	merge
	更新
	remove :删除
	find/JetRefrence :
	根据id查询
4.增删改查操作
5.提交事务
6.释放资源
JpaUtil 工具类
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class JpaUtils {

	private static final EntityManagerFactory factory;

	private JpaUtils() {
	}

	static {
		factory = Persistence.createEntityManagerFactory("myPersistenceUnit");
	}

	public static EntityManager getEntityManager() {
		return factory.createEntityManager();
	}
	
	public static void close() {
		factory.close();
	}

}
简单CRUD
// 新增
public static void main(String[] args) {
	EntityManager em = JpaUtils.getEntityManager();
	EntityTransaction tx = em.getTransaction();
	tx.begin();
	User user = new User();
	user.setUsername("huihui");
	user.setPassword("233");
	// insert into user (create_time, password, username) values (?, ?, ?)
	em.persist(user);
	tx.commit();
	em.close();
}

// 查询
public static void main(String[] args) {
	EntityManager em = JpaUtils.getEntityManager();
	EntityTransaction tx = em.getTransaction();
	tx.begin();
	// 根据id查询数据
	User user = em.find(User.class, 1);
	System.out.println(user);
	tx.commit();
	em.close();
}

// 删除
public static void main(String[] args) {
	EntityManager em = JpaUtils.getEntityManager();
	EntityTransaction tx = em.getTransaction();
	tx.begin();
	// find
	User user = em.find(User.class, 3);
	// delete from user where id=?
	em.remove(user);
	tx.commit();
	em.close();
}

// 更新
public static void main(String[] args) {
	EntityManager em = JpaUtils.getEntityManager();
	EntityTransaction tx = em.getTransaction();
	tx.begin();
	// find
	User user = em.find(User.class, 4);
	user.setPassword("789");
	// update user set create_time=?, password=?, username=? where id=?
	em.merge(user);
	tx.commit();
	em.close();
}

延迟加载与立即加载

// 立即加载
// 根据 id 查询数据
// 立即发送 sql 查询数据
User user = em.find(User.class, 1);
System.out.println(user);
// 延迟加载
// 根据 id 查询数据
// 不会立即发送 sql 查询数据,当在使用到 u2 对象时再查询数据
User u2 = em.getReference(User.class, 2);
System.out.println(u2);
复杂CRUD

JPQL

JPQL全称Java Persistence Query Language
基于首次在EJB2.0中引入的EJB查询语言(EJB QL) , Java持久化查询语言(JPQL)是一种可移
植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起使
用这种语言编写的查询是可移植的,可以被編译成所有主流数据库服务器上的SQL
其特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。

SQL查询的是表和表中的字段
JPQL查询的是实体类和类中的属性
// 查询全部
public static void main(String[] args) {
	EntityManager em = JpaUtils.getEntityManager();
	EntityTransaction tx = em.getTransaction();
	tx.begin();
	
	// 查询全部
	// String jpql = "from cn.liuweiwei.domain.User";
	String jpql = "from User";
	Query query = em.createQuery(jpql);
	@SuppressWarnings("unchecked")
	List<User> users = query.getResultList();
	users.forEach(user -> System.out.println(user));
	
	tx.commit();
	em.close();
}

// 倒序
String jpql = "from User order by id desc";

// 统计查询
public static void main(String[] args) {
	EntityManager em = JpaUtils.getEntityManager();
	EntityTransaction tx = em.getTransaction();
	tx.begin();
	// 统计查询
	String jpql = "select count(id) from User";
	Query query = em.createQuery(jpql);
	Object count = query.getSingleResult();
	System.out.println(count);
	
	tx.commit();
	em.close();
}

// 分页查询
public static void main(String[] args) {
	EntityManager em = JpaUtils.getEntityManager();
	EntityTransaction tx = em.getTransaction();
	tx.begin();
	// 分页查询
	String jpql = "from User";
	Query query = em.createQuery(jpql);
	// limit ?, ? (limit 起始索引, 每页条数)
	// 起始索引
	query.setFirstResult(0);
	// 每页条数
	query.setMaxResults(2);
	@SuppressWarnings("unchecked")
	List<User> users = query.getResultList();
	users.forEach(user -> System.out.println(user));

	tx.commit();
	em.close();
}

// 条件查询
public static void main(String[] args) {
	EntityManager em = JpaUtils.getEntityManager();
	EntityTransaction tx = em.getTransaction();
	tx.begin();
	// 条件查询
	// 查询用户名中包含 i 的数据
	String jpql = "from User where username like ?0";
	Query query = em.createQuery(jpql);
	// 设置第一个参数
	// 方法的第一个参数对应占位符后的数字
	query.setParameter(0, "%i%");
	@SuppressWarnings("unchecked")
	List<User> users = query.getResultList();
	users.forEach(user -> System.out.println(user));

	tx.commit();
	em.close();
}

// 条件查询
// 查询 id > 3 的数据
String jpql = "from User where id > ?1";
Query query = em.createQuery(jpql);
query.setParameter(1, 3);
@SuppressWarnings("unchecked")
List<User> users = query.getResultList();
users.forEach(user -> System.out.println(user));

SpringDataJPA

Spring Data JPA是Spring基于ORM框架、JPA 规范的基础上封装的一套 JPA应用框架,可使开
发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且
易于扩展!学习并使用Spring Data JPA可以极大提高开发效率。
Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的
工作工程中,推荐使用Spring Data JPA + ORM (如,hibernate) 完成操作,这样在切换不同的ORM
框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦。

springData Jpa 极大简化了数据库访问层代码。如何简化的呢? 使用了SpringDataJpa,
我们的dao层中只需要写接口,就自动具有了增删改查、分页查询等方法。

SpringDataJPA, Hibernate JPA 的关系

JPA是一套规范,内部是有接口和抽象类组成的。hibernate 是一套成熟的ORM框架,而且
Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式,我们使用JPA
的API编程,意味着站在更高的角度上看待问题(面向接口编程)。

Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门
用来进行数据持久化的解决方案。
pom.xml
<dependencies>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.12</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.data</groupId>
		<artifactId>spring-data-jpa</artifactId>
		<version>2.1.7.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-test</artifactId>
		<version>5.1.7.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-entitymanager</artifactId>
		<version>5.3.7.Final</version>
	</dependency>

	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-c3p0</artifactId>
		<version>5.3.7.Final</version>
	</dependency>

	<dependency>
		<groupId>log4j</groupId>
		<artifactId>log4j</artifactId>
		<version>1.2.17</version>
	</dependency>
	<dependency>
		<groupId>javax.el</groupId>
		<artifactId>javax.el-api</artifactId>
		<version>2.2.4</version>
	</dependency>
	<dependency>
		<groupId>org.glassfish.web</groupId>
		<artifactId>javax.el</artifactId>
		<version>2.2.4</version>
	</dependency>

	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.47</version>
	</dependency>
</dependencies>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!--1 配置数据源 -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName"
			value="com.mysql.jdbc.Driver" />
		<property name="username" value="root" />
		<property name="password" value="123" />
		<property name="url" value="jdbc:mysql://localhost:3306/data?characterEncoding=utf8" />
	</bean>

	<!--2 配置EntityManagerFactory -->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean
				class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
		</property>
		<!-- 配置实体类所在的包 -->
		<property name="packagesToScan" value="cn.liuweiwei.domain" />

		<property name="jpaProperties">
			<props>
				<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.format_sql">true</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
			</props>
		</property>
	</bean>

	<!--3 配置事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory"
			ref="entityManagerFactory" />
	</bean>

	<!--4 配置支持注解的事务 -->
	<tx:annotation-driven
		transaction-manager="transactionManager" />

	<!--5 配置接口所在的包 -->
	<jpa:repositories base-package="cn.liuweiwei.mapper"
		entity-manager-factory-ref="entityManagerFactory" />

	<!-- 包扫描 -->
	<context:component-scan base-package="cn.liuweiwei" />
</beans>
实体类和测试
@Entity
@Table(name = "user")
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	private String username;
	private String password;
	@Column(name = "create_time")
	private Date createTime;
	// seter/geter...
}
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import cn.liuweiwei.domain.User;

/*
 * JpaRepository 封装了基本查询
 * JpaSpecificationExecutor 封装了复杂查询
 * */

public interface UserMapper extends JpaRepository<User, Integer>, //
		JpaSpecificationExecutor<User> {

}

测试

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AppTest {
	
	@Autowired
	private UserMapper userMapper;
	
	@Test
	public void contextApp() {
		Optional<User> optional = userMapper.findById(1);
		User user = optional.get();
		System.out.println(user);
	}

}
简单CRUD
// 查询
@Test
public void contextApp() {
	// 根据 id 查询数据
	Optional<User> optional = userMapper.findById(1);
	User user = optional.get();
	System.out.println(user);
}

// 查询所有
@Test
public void testFindAll() {
	List<User> users = userMapper.findAll();
	users.forEach(user -> System.out.println(user));
}

// 统计查询
@Test
public void testCount() {
	System.out.println(userMapper.count());
	// 判断 id 为 4的数据是否存在
	System.out.println(userMapper.existsById(4)); // true
}

// 保存或更新
@Test
public void testSave() {
	User user = new User();
	user.setUsername("huahua");
	user.setPassword("789");
	// 更新或保存一条数据
	// 如果 id 为空就保存,否则就更新
	userMapper.save(user);
}

// 删除
@Test
public void testDelete() {
	userMapper.deleteById(6);
}

在这里插入图片描述

JPQL CRUD
public interface UserMapper extends JpaRepository<User, Integer>, //
		JpaSpecificationExecutor<User> {

	@Query("from User where username = ?1")
	User findByName(String username);
	
	@Query("from User")
	List<User> findAllUser();
	
	@Query("from User where username = ?1 and password = ?2")
	User findOne(String username, String password);

	@Query("from User where username like ?1")
	List<User> likeFind(String username);
	
	@Modifying
	@Transactional
	@Query("update User set username = ?1 where id = ?2")
	void updateUser(String username, Integer id);

	@Modifying
	@Transactional
	@Query("delete from User where id = ?1")
	void deleteUser(Integer id);
}

方法名规则映射查询

User findByUsername(String username);

List<User> findByUsernameLike(String username);

User findByUsernameLikeAndPassword(String username, String password);
动态查询
@Test
public void testFindOne() {
	/*
	 * 自定义查询条件
	 * 	实现 Specification接口(提供泛型,查询的对象类型)
	 * 	实现 toPredicate 方法(构造查询条件)
	 * 	借助方法中的两个参数
	 * 		root:获取需要查询的对象属性
	 * 		criteriaBuilder:构造查询条件,模糊/精准匹配
	 */
	@SuppressWarnings("serial")
	Specification<User> spec = new Specification<User>() {
		@Override
		public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, //
				CriteriaBuilder criteriaBuilder) {
			// 获取比较属性
			Path<Object> password = root.get("password");
			// 构造查询条件
			return criteriaBuilder.equal(password, "123");
		}
	};
	Optional<User> optional = userMapper.findOne(spec);
	System.out.println(optional.get());
}

多条件拼接

@Test
public void testFindOneBySelect() {
	@SuppressWarnings("serial")
	Specification<User> spec = new Specification<User>() {
		@Override
		public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, //
				CriteriaBuilder criteriaBuilder) {
			Path<Object> username = root.get("username");
			Path<Object> password = root.get("password");
			return criteriaBuilder.and(criteriaBuilder.equal(username, "vivivi"),//
					criteriaBuilder.equal(password, "123"));
		}
	};
	System.out.println(userMapper.findOne(spec).get());
}

排序

@Test
public void testSort() {
	// 根据 id 降序排序
	userMapper.findAll(new Sort(Direction.DESC, "id")) //
			.forEach(user -> System.out.println(user));
}

分页

@Test
public void testPage() {
	Page<User> page = userMapper.findAll(PageRequest.of(0, 2));
	List<User> users = page.getContent();
	users.forEach(user -> System.out.println(user));
}
多表查询

一对多
Customer.java

@Entity
@Table(name = "customer")
public class Customer {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "c_id")
	private Integer id;
	private String name;
	// 行业
	private String industry;
	private String phone;
	private String address;

	// @OneToMany(targetEntity = Linker.class)
	// @JoinColumn(name = "cust_id", referencedColumnName = "c_id")
	@OneToMany(mappedBy = "customer") // 放弃外键维护权
	private List<Linker> linkers = new ArrayList<>();

	// seter/geter...
}

Linker.java

@Entity
@Table(name = "linker")
public class Linker {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	private String name;
	private String phone;
	private String gender;
	private String position;

	@ManyToOne(targetEntity = Customer.class)
	@JoinColumn(name = "cust_id", referencedColumnName = "c_id")
	private Customer customer;
	// seter/geter...
	
}

Test

@Test
@Transactional
@Rollback(false)
public void testSave() {
	Customer customer = new Customer();
	customer.setName("字节跳动");
	customer.setPhone("110");
	customer.setIndustry("互联网");
	customer.setAddress("北京市朝阳区");
	
	Linker linker = new Linker();
	linker.setName("冰冰");
	linker.setGender("女");
	linker.setPhone("112");
	linker.setPosition("前端工程师");

	// 建立联系
	linker.setCustomer(customer);
	// 保存
	customerMapper.save(customer);
	linkerMapper.save(linker);
}

在这里插入图片描述
在这里插入图片描述
级联删除

删除从表数据:可以随时任意删除。

删除主表数据:。
	有从表数据。
		1、在默认情况下,它会把外健字段置为null, 然后删除主表数据。如果在数据库的表
		结构上,外键字段有非空約東,默认情况就会报错了。
		2、如果配置了放弃维护关联关系的权利,则不能删除(与外键字段是否允许为null,没
		有关系)因为在删除时,它根本不会去更新从表的外键字段了。
		3、如果还想删除,使用级联删除引用。
		
	没有从表数据引用,随便删。

在实际开发中,级联删除请慎用! (在一对多的情况下)。

// cascade = CascadeType.ALL 配置级联
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL) 
private List<Linker> linkers = new ArrayList<>();

@Test
public void testDelete() {
	// 级联删除
	customerMapper.delete(customerMapper.findById(1).get());
}

多对多
Person.java

@Entity
@Table(name = "person")
public class Person {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "p_id")
	private Integer id;
	private String username;
	private String password;

	// 配置用户到角色多对多
	// fetch = FetchType.EAGER 关闭延迟加载
	@ManyToMany(targetEntity = Role.class, //
			cascade = CascadeType.ALL, //
			fetch = FetchType.LAZY)
	// 配置中间表,包含两个外键
	@JoinTable(
			// 中间表名
			name = "person_role",
			// 当前对象 Person 在中间表中的外键
			joinColumns = { //
					@JoinColumn(name = "sys_p_id", referencedColumnName = "p_id") //
			},
			// 对方对象 Role 在中间表中的外键
			inverseJoinColumns = { //
					@JoinColumn(name = "sys_r_id", referencedColumnName = "r_id")//
			}//
	)
	private List<Role> roles = new ArrayList<Role>();
	// getter.setter...

	// 注意 toString() 中不能包含 roles 属性
	@Override
	public String toString() {
		return "Person [id=" + id + ", username=" + username + ", password=" + password + "]";
	}

}

Role.java

@Entity
@Table(name = "role")
public class Role {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "r_id")
	private Integer id;
	private String name;

	// 配置角色到用户多对多
	@ManyToMany(mappedBy = "roles", //
			cascade = CascadeType.ALL, //
			fetch = FetchType.LAZY)
	private List<Person> persons = new ArrayList<Person>();
	// getter/setter...
	
	// 注意这个 toString() 方法,不能打印 person 属性!!!
	// 否则会出现 stackoverflow !!
	@Override
	public String toString() {
		return "Role [id=" + id + ", name=" + name + "]";
	}
}

Test.java

@Test
@Transactional
@Rollback(false)
public void testSave() {
	Person person = new Person();
	person.setUsername("冰冰");
	person.setPassword("233");

	Role role = new Role();
	role.setName("前端工程师");
	// 关联关系
	person.getRoles().add(role);
	personMapper.save(person);
	roleMapper.save(role);
}

@Test
@Transactional // 必须加这个注解才能使延迟加载过程中不关闭 session
public void testFindById() {
	Person person = personMapper.findById(1).get();
	// List<Role> roles = person.getRoles();
	System.out.println(person);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值