首页 文章 精选 留言 我的

精选列表

搜索[Spring Framework],共10000篇文章
优秀的个人博客,低调大师

Spring Data开发手册|Java持久化API(JPA)需要了解到什么程度呢?

JPA,Java Persistence API是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合ORM技术 ORM:通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。本质就是将数据从一种形式转换到另外一种形式。 同时也结束了Hibernate、TopLink等ORM框架各自为营的局面。JPA充分吸收了Hibernate、TopLink等ORM框架的基础上发展起来的,使用方便,伸缩性强 注意: JPA不是一种新的ORM框架,它的出现只是用于规范现有的ORM技术,它不能取代现有的Hibernate等ORM框架,相反,采用JPA开发时,我们仍将使用这些ORM框架,只是此时开发出来的应用不在依赖于某个持久化提供商。应用可以在不修改代码的情况下载任何JPA环境下运行,真正做到低耦合,可扩展的程序设计。类似于JDBC,在JDBC出现以前,我们的程序针对特性的数据库API进行编程,但是现在我们只需要针对JDBC API编程,这样能够在不改变代码的情况下就能换成其他的数据库。 JPA是一套规范,不是一套产品。Hibernate是一套产品,如果这些产品实现了JPA规范,那么我们可以叫它们为JPA的实现产品。使用JPA,就可以把我们的应用从Hibernate中解脱出来,那么现在问题来了::如何使用JPA来开发呢? 准备好了吗,进入正题,起飞! 首先,先带大家看一下本篇文章的大致介绍。 没目录怎么知道这篇到底有多少干货呢? 以前的开发模式 JPA是什么 JPA解决了什么问题 JPA的第一个HelloWord程序 详解配置文件 常用的注解 一对一的问题 一对多的问题 多对多的问题 JPA中常见的方法 JPA中对象的状态 注意事项 是不是很清晰呢,什么?还不进入正文,来了,安排上,一个一个来: 回顾以前的开发模式 以前开发的时候我们的DAO层,要么使用Hibernate、要么使用iBatis、dbutils、toplink 需求:假设现在的产品的1.0版本的DAO的实现使用的是Hibernate、现在老板要求将DAO层换成TopLink 按照现在的解决方案整个DAO层都是需要重写的,很耗费人力和物力,增加了成本 有没有一种方案?这种方案就是如果我们需要换ORM框架,我们的整个DAO层都不需要改变只是需要改变配置文件就可以了呢? JPA技术技术因此而生 JPA是什么 JPA实际上是sun公司出的一套规范、这套规范的作用是为了解决市场上ORM框架一家独大的问题 JPA是一套规范,只要我们的ORM框架实现了这套规范,那么在使用这个ORM框架的时候,就不需要面对于某一种ORM产品的API来进行编程,而是统一的面向于JPA来进行编程,这个时候即使你的ORM产品改变了,那么你的DAO层面向于JPA编程的代码是不用变的 JPA解决了什么问题 JPA统一了ORM框架访问数据库的API JPA解决了ORM框架一家独大的问题 JPA的第一个HelloWorld程序 导包 编写配置文件 <?xmlversion="1.0"encoding="UTF-8"?> <persistencexmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <persistence-unitname="hibernateJPA"transaction-type="RESOURCE_LOCAL"> <properties> <propertyname="hibernate.hbm2ddl.auto"value="update"></property> <propertyname="hibernate.show_sql"value="true"></property> <propertyname="hibernate.format_sql"value="true"></property> <propertyname="hibernate.dialect"value="org.hibernate.dialect.MySQL5Dialect"></property> <propertyname="hibernate.connection.url"value="jdbc:mysql:///qianyu"></property> <propertyname="hibernate.connection.driver_class"value="com.mysql.jdbc.Driver"></property> <propertyname="hibernate.connection.username"value="root"></property> <propertyname="hibernate.connection.password"value="root"></property> </properties> </persistence-unit> </persistence> 编写Java实体和注解 @Table(name="t_user")//设置当前的类的对象对应的表名字 @Entity//表示当前的这个类是一个持久化的实体 publicclassUser{ @Id//这个表示的是当前的字段是主键 @GeneratedValue(strategy=GenerationType.IDENTITY)//这个表示的是主键的生成策略(自增长) @Column(name="uId") privateintuId; @Column(name="userName")//列的名字 privateStringuserName; @Column(name="password") privateStringpassword; } 测试 @Test publicvoidtestHelloWorld()throwsException{ //第一步:创建实体管理的工厂 EntityManagerFactoryef=Persistence.createEntityManagerFactory("hibernateJPA"); //通过工厂创建实体的管理器 EntityManagerem=ef.createEntityManager(); //第三步:开启事务 em.getTransaction().begin(); //操作业务逻辑 Useruser=newUser(); user.setUserName("浅羽"); user.setPassword("123"); //保存用户实体到数据库 em.persist(user); //提交事务 em.getTransaction().commit(); //关闭管理器 em.close(); ef.close(); } 详解配置文件 <?xmlversion="1.0"encoding="UTF-8"?> <persistencexmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <!-- persistence-unit:这个叫做持久化的单元这个的作用就是配置访问数据库的信息 name:逻辑意义上可以随便写但是一般情况下见名之意这个一般情况下写JPA的实现产品的名字 transaction-type:事务的类型 RESOURCE_LOCAL:局部事务 事务的分类 全局事务 举例:张三给李四转账(建设银行----中国银行) 局部事务 举例:所有的操作在同一个库里头执行 粗粒度事务 举例:能够对整个类或者方法体进行事务的管理 细粒度事务 举例:就是能够对某几行代码进行事务的管理 --> <persistence-unitname="hibernateJPA"transaction-type="RESOURCE_LOCAL"> <properties> <propertyname="hibernate.hbm2ddl.auto"value="update"></property> <propertyname="hibernate.show_sql"value="true"></property> <propertyname="hibernate.format_sql"value="true"></property> <propertyname="hibernate.dialect"value="org.hibernate.dialect.MySQL5Dialect"></property> <propertyname="hibernate.connection.url"value="jdbc:mysql:///qianyu"></property> <propertyname="hibernate.connection.driver_class"value="com.mysql.jdbc.Driver"></property> <propertyname="hibernate.connection.username"value="root"></property> <propertyname="hibernate.connection.password"value="root"></property> </properties> </persistence-unit> </persistence> 常用的注解线程池技术 @Table:表示的是当前的实体对应的数据库中的表名字 @Entity:表示的是当前的实体是一个持久化的实体 @Id:这个表示当前的属性是一个主键 @GeneratedValue:主键的生成策略 strategy=GenerationType.IDENTITY:这个表示的是主键自增长 strategy=GenerationType.AUTO:使用表来生成目标表的主键 strategy=GenerationType.SEQUENCE:使用序列来生成主键 @Column:jAVA的属性对应的数据库表的列的名字 Name:名字 Length:表示的是字段的长度 nullable=false:这个表示的是不能为null unique=true:是否是唯一的 @Transient:当前字段在数据库中不对应列 @Enumerated:表示的是枚举在数据库中的映射使用下标还是字符串 EnumType.STRING:表示的是以字符串的形式显示 EnumType.ORDINAL:表示枚举在数据中以下标的形式显示 @Lob:修饰String类型的时候表示的大文本 修饰byte[]的时候表示存储的是二进制 一对一的问题 需求:一个人对应了一个身份证、一个身份证也唯一对应了一个人 身份证----->人 一对一的关系 代码演示: 声明IdCard类: @Entity @Table publicclassIdCard{ @Id privateStringcardNum; privateDatestartTime; privateDateendTime; //一个身份证唯一的对应了一个人 @OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY) @JoinColumn(name="pId")//这个表示的是添加一个列这个列映射下面对象中的这个Id privatePeoplepeople; } 声明People类: @Entity @Table publicclassPeople{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) privateintpId; privateStringpName; privateStringpTel; //一个人对应了一个身份证 //在关联关系中配置了mappedBy的哪一方没有权限维护另外一方 //mappedBy的值就是当前的类在下面对象中声明的这个名字 @OneToOne(mappedBy="people",cascade=CascadeType.ALL) privateIdCardidCard; } 测试: @Test publicvoidtestHelloWorld()throwsException{ EntityManagerentityManager=JPAUtils.getEntityManager(); IdCardidCard=newIdCard(); idCard.setCardNum("510...x"); idCard.setStartTime(newDate()); idCard.setEndTime(newDate()); Peoplepeople=newPeople(); people.setpName("小羽"); people.setpTel("1234566"); idCard.setPeople(people); entityManager.persist(idCard); JPAUtils.close(); } 一对多的问题 需求:部门和员工的对应 部门----->员工 一对多的关联关系 代码演示: 声明部门对象: @Entity @Table publicclassDept{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) privateintdId; privateStringdName; privateStringdDes; //一个部门有多个员工 @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy="dept") privateSet<Employee>emps; } 声明员工对象: @Entity @Table publicclassEmployee{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) privateintempId; privateStringempName; @ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY) @JoinColumn(name="dId") privateDeptdept; } 测试: @Test publicvoidtestOne2Many()throwsException{ EntityManagerentityManager=JPAUtils.getEntityManager(); Employeeemp=newEmployee(); emp.setEmpName("小娜"); Deptdept=newDept(); dept.setdName("研发部"); dept.setdDes("专门搞开发的"); emp.setDept(dept); entityManager.persist(emp); JPAUtils.close(); } 多对多的问题 需求:一个学生可以被多个老师教,一个老师也可以教多个学生 学生----->老师一对多 老师----->学生一对多 老师和学生的最终关系多对多的关联关系 代码演示: 编写老师实体: @Entity @Table publicclassTeacher{ @Id privateStringtNum; privateStringtName; @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY) @JoinTable(name="t_teacher_student", joinColumns=@JoinColumn(name="tNum"),//映射的是当前这个类的主键 inverseJoinColumns={@JoinColumn(name="stuNum")})//映射的是对方表的主键 privateSet<Student>students; } 编写学生实体: @Entity @Table publicclassStudent{ @Id privateintstuNum; privateStringstuName; privateintage; @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy="students") privateSet<Teacher>teachers; } 测试: @Test publicvoidtestMany2Many()throwsException{ EntityManagerem=JPAUtils.getEntityManager(); Teacherteacher=newTeacher(); teacher.settName("小羽"); teacher.settNum("007"); Set<Student>students=newHashSet<Student>(); Studentstudent=newStudent(); student.setAge(18); student.setStuName("小白"); student.setStuNum(100); Studentstudent1=newStudent(); student1.setAge(19); student1.setStuName("小娜"); student1.setStuNum(1000); Studentstudent2=newStudent(); student2.setAge(20); student2.setStuName("小黑"); student2.setStuNum(10000); students.add(student); students.add(student1); students.add(student2); teacher.setStudents(students); em.persist(teacher); JPAUtils.close(); } JPA中常见的方法 代码演示: 常见方法: publicvoidtestMethod()throwsException{ EntityManagerentityManager=JPAUtils.getEntityManager(); Useruser=newUser(); user.setUserName("小灰"); user.setuId(1); //添加数据的方法 //entityManager.persist(user); //查询数据 //Useruser2=entityManager.find(User.class,1); //这个写的是HQL语句 //Queryquery=entityManager.createQuery("fromUser"); //Listlist=query.getResultList(); //下面这个方法有主键值那么就修改没有主键值就插入 //entityManager.merge(user); /*创建的是本地SQL的查询 Queryquery=entityManager.createNativeQuery("select*fromuser"); Listlist=query.getResultList();*/ //一般用在查询中获取最新的这个数据 //entityManager.refresh(user); Useruser2=entityManager.find(User.class,1); entityManager.remove(user2); //System.out.println(list); JPAUtils.close(); } JPA中对象的状态 对象的状态: 新建状态: User user = new User();和数据库以及内存没有任何关联,对象仅仅是被new出来之后的这种状态 托管状态:对象调用了find persist refresh merge或者查询之后的这个对象状态就叫做托管状态,托管状态的数据是被entityManager管理的,并且内存和数据库的数据是对应了,这个时候如果你改变了内存的这个数据的话,并且进行提交的话,那么这个数据会和数据库进行同步 游离状态: 当前的对象调用了clear方法之后在close方法之前的这段时间,这个对象处于游离状态。clear:表示的是清楚内存和数据库数据的对应的关系 删除状态:当前对象close之后的对象的这种状态,就称为删除状态 注意事项 表名不写默认就是类作为表名 column不写,表的列名就是类的属性名 @GeneratedValue后面值不写默认是auto 结语 JPA是我们开发中离不开的经常用到的技术,其涉及的技术和知识面其实远不止上面列出的这些。 后续浅羽会继续更新关于JPA的开发知识,只希望能对大家有所帮助,谢谢大家的支持! 点点点,一键三连都在这儿!

优秀的个人博客,低调大师

[Spring cloud 一步步实现广告系统] 20. 系统运行测试

系统运行 经过长时间的编码实现,我们的主体模块已经大致完成,因为之前我们都是零散的对各个微服务自行测试,接下来,我们需要将所有的服务模块进行联调测试,Let's do it. 清除测试数据&测试文件 我们在实现各个服务的过程中,添加了不少的测试文件和测试数据,为了不影响我们最终的展示效果,我们先将之前的历史数据清理掉。 drop database advertisement; 依然使用flyway 添加我们的测试数据: INSERT INTO `ad_user` VALUES (10,'Isaac','B2E56F2420D73FEC125D2D51641C5713',1,'2019-08-14 20:29:01','2019-08-14 20:29:01'); INSERT INTO `ad_creative` VALUES (10,'第一个创意',1,1,720,1080,1024,0,1,10,'https://www.life-runner.com','2019-08-14 21:31:31','2019-08-14 21:31:31'); INSERT INTO `ad_plan` VALUES (10,10,'推广计划名称',1,'2019-11-28 00:00:00','2019-11-20 00:00:00','2019-11-19 20:42:27','2019-08-14 20:57:12'); INSERT INTO `ad_unit` VALUES (10,10,'第一个推广单元',1,1,10000000,'2019-11-20 11:43:26','2019-11-20 11:43:26'),(12,10,'第二个推广单元',1,1,15000000,'2019-01-01 00:00:00','2019-01-01 00:00:00'); INSERT INTO `ad_unit_district` VALUES (10,10,'陕西省','西安市'),(11,10,'陕西省','西安市'),(12,10,'陕西省','西安市'),(14,10,'山西省','阳泉市'); INSERT INTO `ad_unit_hobby` VALUES (10,10,'爬山'),(11,10,'读书'),(12,10,'写代码'); INSERT INTO `ad_unit_keyword` VALUES (10,10,'汽车'),(11,10,'火车'),(12,10,'飞机'); INSERT INTO `relationship_creative_unit` VALUES (10,10,10); 导出测试索引文件 可参考 全量索引传送门 ,或者下载源码github传送门 / gitee传送门 ,运行mscx-ad-db项目,然后执行 http://localhost:7002/ad-db/export/plan。 开发自测 Unit Test 一个合格的开发人员是绝对不能容忍自己的代码存在傻~ bug 存在的,但是个人总会有犯错的时候,那么我们要怎么避免此类非业务发展导致的基础问题呢,这时候,开发的UT就显得非常Important了。 广告投放系统测试 我们来编写投放系统的单元测试,如下图:单元测试模块的目录结构与我们的正式项目结构保持一致,如果你需要给单元测试编写特例化配置,把我们的application.yml配置文件copy到UT中就可以了,这里就不做赘述。 用户服务单元测试 /** * UserServiceTest for 用户服务单元测试 * * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */ @RunWith(SpringRunner.class) @SpringBootTest( classes = {SponsorApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE ) public class UserServiceTest { @Autowired private IUserService userService; @Test // @Transactional public void testCreateUser() throws AdException { UserRequestVO userRequestVO = new UserRequestVO("Isaac Zhang"); UserResponseVO responseVO = userService.createUser(userRequestVO); assert responseVO.getUserName() == "Isaac Zhang"; System.out.printf("创建用户: %s", JSON.toJSONString(responseVO)); } } 大家可以看到,在上述代码中,我们测试了创建用户的service方法,特别注意2个点: @Transactional注解. 因为我们使用的是和正式服务相同的数据库,我们在测试的时候就会真实的插入一个用户到ad_user表中,如果我们不想这个用户存入表中,就需要加上@Transactional注解,我们的创建就不会commit,也就不会被插入到真实数据库中。 @SpringBootTest 注解 classes表明测试启动类是哪个,webEnvironment = SpringBootTest.WebEnvironment.NONE表明我们当前的测试并非一个web环境。 这里就不针对每一个service进行单元测试的编写,但是大家一定要记住,在真实的企业开发环境中,大的开发团队一定会对单元测试的代码覆盖率有一个要求,一般都不会低于60%,我个人对自己的行代码覆盖率是 > 80%.这样才能真实的保证我们的每一个方法都尽量都执行和验证到。 大家尝试依次实现其余的单元测试吧。 广告检索系统测试 我们的检索服务对外只提供一个服务,因此我们只需要创建一个Test类就可以了,let's code. package com.sxzhongf.ad.search; import com.sxzhongf.ad.AdSearchApplication; import com.sxzhongf.ad.search.vo.SearchRequest; import com.sxzhongf.ad.search.vo.SearchResponse; import com.sxzhongf.ad.search.vo.feature.DistrictFeature; import com.sxzhongf.ad.search.vo.feature.FeatureRelation; import com.sxzhongf.ad.search.vo.feature.HobbyFeatrue; import com.sxzhongf.ad.search.vo.feature.KeywordFeature; import com.sxzhongf.ad.search.vo.media.AdSlot; import com.sxzhongf.ad.search.vo.media.App; import com.sxzhongf.ad.search.vo.media.Device; import com.sxzhongf.ad.search.vo.media.Geo; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * SearchTest for 搜索服务测试用例 * * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */ @RunWith(SpringRunner.class) @SpringBootTest(classes = AdSearchApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) public class SearchTest { @Autowired private ISearch search; @Test public void testFetchAds() { SearchRequest request = new SearchRequest().builder() .mediaId("isaac-search-mediaId") .requestInfo(new SearchRequest.RequestInfo( "request id", Arrays.asList( new AdSlot().builder() .adSlotCode("slot code") .height(800) .minCpm(1024) .positionType(1) .type(Arrays.asList(1)) .build() ), buildSimpleApp(), buildSimpleGeo(), buildSimpleDevice() )) .featureInfo( buildSimpleFeatureInfo( Arrays.asList("汽车", "火车", "飞机"), Collections.singletonList( new DistrictFeature.ProvinceAndCity( "陕西省", "西安市" ) ), Arrays.asList("爬山", "写代码", "飞机"), FeatureRelation.OR ) ) .build(); SearchResponse response = search.fetchAds(request); // assert response.adSlotRelationAds.get(0).contains("key"); System.out.println("开始查询广告拉:" + response); } /** * 创建demo {@link App} */ private App buildSimpleApp() { return new App().builder() .activityName("simple App activityName") .appCode("simple App appCode") .appName("simple app name") .packageName("simple app package name") .build(); } /** * 创建demo {@link Geo} */ private Geo buildSimpleGeo() { return new Geo().builder() .longitude(Float.valueOf("100.2222222")) .latitude(Float.valueOf("38.8888888")) .city("xiaan") .province("shaanxi") .build(); } /** * 创建demo {@link Device} */ private Device buildSimpleDevice() { return new Device().builder() .deviceCode("simple device code") .deviceMacAddr("simple mac addr") .displaySize("simple display size") .ip("127.0.0.1") .model("simple model") .screenSize("simple screen size") .serialName("simple serial name") .build(); } private SearchRequest.FeatureInfo buildSimpleFeatureInfo( List<String> keywords, List<DistrictFeature.ProvinceAndCity> provinceAndCities, List<String> hobbys, FeatureRelation featureRelation ) { return new SearchRequest.FeatureInfo( new KeywordFeature(keywords), new DistrictFeature(provinceAndCities), new HobbyFeatrue(hobbys), featureRelation ); } } 在这个测试用例中,我们主要的复杂性是在组件各种查询条件,这个就需要各位伙伴在理解业务的时候需要万分上心。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册