创建表sql:
CREATE TABLE `t_user` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`user_name` varchar(60) NOT NULL,
`sex` int(3) NOT NULL DEFAULT '1' check (sex in(1,2)),
`note` varchar(256) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;
在maven中引入spring-boot-starter-data-jpa,接着创建pojo与表对应起来:
package com.example.JPAdemo;
//import org.springframework.data.annotation.Id;
import javax.persistence.*;
@Entity(name="user")
@Table(name = "t_user")
public class User {
@Id//主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键策略,递增
private int id;
@Column(name = "user_name")
private String userName;
@Convert(converter = SexConverter.class)
private SexEnum sex;
private String note;
*****getter and setter *****
}
使用@Entity标明这是一个实体类,@Table配置的属性name指出它映射数据库的表,这样实体就映射到了对应的表上,@Id标注那个属性为表的主键,@GeneratedValue则是可以配置采用何种策略生成主键,@Column标注用户名,因为属性名称(userName)和数据库列名(user_name)不一致,而其他属性名称和数据库列名保持一致,这样就能与数据库表字段一一对应起来了。这里的性别需要转换,使用@Convert指定了SexConverter作为转换器,SexConverter代码:
package com.example.JPAdemo;
import javax.persistence.AttributeConverter;
public class SexConverter implements AttributeConverter {
//将枚举转换为数据库列
@Override
public Integer convertToDatabaseColumn(SexEnum sexEnum) {
return sexEnum.getId();
}
//将数据库列转换为枚举
@Override
public SexEnum convertToEntityAttribute(Integer id) {
return SexEnum.getEnumById(id);
}
}
SexEnum代码:
package com.example.JPAdemo;
public enum SexEnum {
MALE(1,"男"),FEMALE(2,"女");
private int id;
private String name;
SexEnum(int id,String name){
this.id=id;
this.name=name;
}
public static SexEnum getEnumById(int id){
for(SexEnum sex:SexEnum.values()){
if (sex.getId()==id){
return sex;
}
}
return null;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
性别转换器SexConverter定义了从数据库读出的转换规则和从属性转换为数据库列的规则,这样就能使性别枚举类与数据库的列对应起来。
有了pojo对象,我们还需要一个jpa接口来定义对应的操作。spring提供了JpaRepository接口提供,它本身继承了其他接口。JPA最顶级的接口是Repository,而它没有定义任何方法,定义方法是它的子接口CrudRepository,实现最基本的增删改操作,功能还不够强大,PagingAndSortingRepository则继承了它并且提供分页和排序功能,最后JpaRepository扩展了PagingAndSortingRepository,并且扩展了QueryByExampleExecutor接口,这样就有按例子查询的功能。一般而言,我们只需要定义JPA借口扩展JpaRepository便可以获得JPA提供的方法了。
定义JPA接口:
package com.example.JPAdemo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface JpaUserRepository extends JpaRepository {
}
这样便拥有了系统默认帮我们实现的方法。这里并不需要提供任何实现类,这些spring会根据JPA接口规范帮我们完成,然后就可以开发控制器——JpaController:
package com.example.JPAdemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/jpa")
public class JpaController {
@Autowired
private JpaUserRepository jpaUserRepository;
@RequestMapping("/getUser")
@ResponseBody
public User getUser(int id){
//使用jpa接口查询对象
User user = jpaUserRepository.findOne(id);
return user;
}
}
配置JPA属性:
#使用MySql数据库方言
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
#打印数据库sql
spring.jpa.show-sql=true
#选择Hibernat数据定义语言(DDL)策略为update
spring.jpa.hibernate.ddl-auto=update
spring boot启动文件:
package com.example.JPAdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories(basePackages = "com.example.JPAdemo")
@EntityScan(basePackages = "com.example.JPAdemo")
@SpringBootApplication
public class JpAdemoApplication {
public static void main(String[] args) {
SpringApplication.run(JpAdemoApplication.class, args);
System.out.println("启动成功");
}
}
这里使用@EnableJpaRepositories启用JPA和制定扫描的包,这样spring就会将对应的JPA接口扫描进来,并且生成对应的bean,装配在IOC容器中,这样就可以在控制器上用 @Autowired进行依赖注入了。通过定义@EntityScan定义的包扫描,就可以扫描装在JPA的实体类了。而实际上就算没有@EntityScan和@EnableJpaRepositories,只要依赖了spring-boot-starter-data-jpa,spring boot2.x也会对项目进行扫描,这样JPA的实体和接口都会被扫描,只是使用它们可以更进一步配置JPA的相关信息而已。
到这一步我们的开发就完了,在数据库里添加一条测试数据
测试结果:
从结果中可以看出控制器已经通过JPA接口查询到了数据,然后将其展现为json。
如果运行中出现这样的报错信息:
Error creating bean with name ‘entityManagerFactory’ defined in class path resource 。。。。。
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: com.example.JPAdemo.User
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1081) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:856) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at com.example.JPAdemo.JpAdemoApplication.main(JpAdemoApplication.java:14) [classes/:na]
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: com.example.JPAdemo.User
at org.hibernate.cfg.InheritanceState.determineDefaultAccessType(InheritanceState.java:265) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.cfg.InheritanceState.getElementsToProcess(InheritanceState.java:211) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:717) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:245) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:222) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:265) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:770) ~[hibernate-entitymanager-5.0.3.Final.jar:5.0.3.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:797) ~[hibernate-entitymanager-5.0.3.Final.jar:5.0.3.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:353) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:373) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:362) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
... 16 common frames omitted
这个需要检查一下User实体类,看@Id引用的包是不是
import org.springframework.data.annotation.Id;
把这个删了就OK了,@Id引用的应该是import javax.persistence.Id;