我是靠谱客的博主 香蕉小猫咪,最近开发中收集的这篇文章主要介绍java8 collections.sort,Java8 Collections.sort(有时)不排序JPA返回的列表,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Java8在我的JPA EclipseLink 2.5.2环境中不断做奇怪的事情.我不得不删除https://stackoverflow.com/questions/26806183/java-8-sorting-behaviour的问题

昨天,因为在这种情况下的排序受到一个奇怪的JPA行为的影响 – 我找到了一个解决方法,通过强制进行最后排序之前的第一个排序步骤.

仍然在Java 8与JPA Eclipselink 2.5.2以下代码有些时候不排序在我的环境(Linux,MacOSX,都使用build 1.8.0_25-b17).它在JDK 1.7环境中工作正常.

public List getDocumentsByModificationDate() {

List docs=this.getDocuments();

LOGGER.log(Level.INFO,"sorting "+docs.size()+" by modification date");

Comparator comparator=new ByModificationComparator();

Collections.sort(docs,comparator);

return docs;

}

当从JUnit测试调用时,上述功能正常工作.

当在生产环境中解除绑定时,我得到一个日志条目:

INFORMATION: sorting 34 by modification date

但是在TimSort中,返回结果为nRemaining< 2被击中 - 所以没有排序. JPA提供的IndirectList(见What collections does jpa return?)被认为是空的.

static void sort(T[] a, int lo, int hi, Comparator super T> c,

T[] work, int workBase, int workLen) {

assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;

int nRemaining = hi - lo;

if (nRemaining < 2)

return; // Arrays of size 0 and 1 are always sorted

此解决方法排序正确:

if (docs instanceof IndirectList) {

IndirectList iList = (IndirectList)docs;

Object sortTargetObject = iList.getDelegateObject();

if (sortTargetObject instanceof List>) {

List sortTarget=(List) sortTargetObject;

Collections.sort(sortTarget,comparator);

}

} else {

Collections.sort(docs,comparator);

}

题:

这是JPA Eclipselink的错误,或者我可以在自己的代码中通常做些什么?

请注意 – 我不能将软件更改为Java8源代码合规性.当前的环境是Java8运行时.

我对这种行为感到惊讶 – 特别令人讨厌的是,测试工具运行正常,而在生产环境中存在问题.

它包含一个具有JUnit测试的http://sscce.org/示例,通过调用em.clear()可以重现问题,从而分离所有对象并强制使用IndirectList.请参阅下面的JUnit案例以供参考.

随着渴望:

// https://stackoverflow.com/questions/8301820/onetomany-relationship-is-not-working

@OneToMany(cascade = CascadeType.ALL, mappedBy = "parentFolder", fetch=FetchType.EAGER)

单位工作.如果使用FetchType.LAZY或在JDK 8中省略了fetch类型,则行为可能与JDK 7不同(现在我必须检查).

为什么?

在这个时候,我假设有一个需要在列表中指定Eager提取或迭代一次,以便在排序前基本上手动获取.还有什么可以做的?

JUnit测试

persistence.xml和pom.xml可以从https://github.com/WolfgangFahl/JPAJava8Sorting获取

测试可以使用MYSQL数据库或DERBY内存(默认值)运行

package com.bitplan.java8sorting;

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.logging.Level;

import java.util.logging.Logger;

import javax.persistence.Access;

import javax.persistence.AccessType;

import javax.persistence.CascadeType;

import javax.persistence.Entity;

import javax.persistence.EntityManager;

import javax.persistence.EntityManagerFactory;

import javax.persistence.FetchType;

import javax.persistence.Id;

import javax.persistence.ManyToOne;

import javax.persistence.OneToMany;

import javax.persistence.Persistence;

import javax.persistence.Query;

import javax.persistence.Table;

import org.eclipse.persistence.indirection.IndirectList;

import org.junit.Test;

/**

* Testcase for

* https://stackoverflow.com/questions/26816650/java8-collections-sort-sometimes-does-not-sort-jpa-returned-lists

* @author wf

*

*/

public class TestJPASorting {

// the number of documents we want to sort

public static final int NUM_DOCUMENTS = 3;

// Logger for debug outputs

protected static Logger LOGGER = Logger.getLogger("com.bitplan.java8sorting");

/**

* a classic comparator

* @author wf

*

*/

public static class ByNameComparator implements Comparator {

// @Override

public int compare(Document d1, Document d2) {

LOGGER.log(Level.INFO,"comparing " + d1.getName() + "<=>" + d2.getName());

return d1.getName().compareTo(d2.getName());

}

}

// Document Entity - the sort target

@Entity(name = "Document")

@Table(name = "document")

@Access(AccessType.FIELD)

public static class Document {

@Id

String name;

@ManyToOne

Folder parentFolder;

/**

* @return the name

*/

public String getName() {

return name;

}

/**

* @param name the name to set

*/

public void setName(String name) {

this.name = name;

}

/**

* @return the parentFolder

*/

public Folder getParentFolder() {

return parentFolder;

}

/**

* @param parentFolder the parentFolder to set

*/

public void setParentFolder(Folder parentFolder) {

this.parentFolder = parentFolder;

}

}

// Folder entity - owning entity for documents to be sorted

@Entity(name = "Folder")

@Table(name = "folder")

@Access(AccessType.FIELD)

public static class Folder {

@Id

String name;

// https://stackoverflow.com/questions/8301820/onetomany-relationship-is-not-working

@OneToMany(cascade = CascadeType.ALL, mappedBy = "parentFolder", fetch=FetchType.EAGER)

List documents;

/**

* @return the name

*/

public String getName() {

return name;

}

/**

* @param name the name to set

*/

public void setName(String name) {

this.name = name;

}

/**

* @return the documents

*/

public List getDocuments() {

return documents;

}

/**

* @param documents the documents to set

*/

public void setDocuments(List documents) {

this.documents = documents;

}

/**

* get the documents of this folder by name

*

* @return a sorted list of documents

*/

public List getDocumentsByName() {

List docs = this.getDocuments();

LOGGER.log(Level.INFO, "sorting " + docs.size() + " documents by name");

if (docs instanceof IndirectList) {

LOGGER.log(Level.INFO, "The document list is an IndirectList");

}

Comparator comparator = new ByNameComparator();

// here is the culprit - do or don't we sort correctly here?

Collections.sort(docs, comparator);

return docs;

}

/**

* get a folder example (for testing)

* @return - a test folder with NUM_DOCUMENTS documents

*/

public static Folder getFolderExample() {

Folder folder = new Folder();

folder.setName("testFolder");

folder.setDocuments(new ArrayList());

for (int i=NUM_DOCUMENTS;i>0;i--) {

Document document=new Document();

document.setName("test"+i);

document.setParentFolder(folder);

folder.getDocuments().add(document);

}

return folder;

}

}

/** possible Database configurations

using generic persistence.xml:

version="2.0">

sorting test

org.eclipse.persistence.jpa.PersistenceProvider

false

*/

// in MEMORY database

public static final JPASettings JPA_DERBY=new JPASettings("Derby","org.apache.derby.jdbc.EmbeddedDriver","jdbc:derby:memory:test-jpa;create=true","APP","APP");

// MYSQL Database

// needs preparation:

// create database testsqlstorage;

// grant all privileges on testsqlstorage to cm@localhost identified by 'secret';

public static final JPASettings JPA_MYSQL=new JPASettings("MYSQL","com.mysql.jdbc.Driver","jdbc:mysql://localhost:3306/testsqlstorage","cm","secret");

/**

* Wrapper class for JPASettings

* @author wf

*

*/

public static class JPASettings {

String driver;

String url;

String user;

String password;

String targetDatabase;

EntityManager entityManager;

/**

* @param driver

* @param url

* @param user

* @param password

* @param targetDatabase

*/

public JPASettings(String targetDatabase,String driver, String url, String user, String password) {

this.driver = driver;

this.url = url;

this.user = user;

this.password = password;

this.targetDatabase = targetDatabase;

}

/**

* get an entitymanager based on my settings

* @return the EntityManager

*/

public EntityManager getEntityManager() {

if (entityManager == null) {

Map jpaProperties = new HashMap();

jpaProperties.put("eclipselink.ddl-generation.output-mode", "both");

jpaProperties.put("eclipselink.ddl-generation", "drop-and-create-tables");

jpaProperties.put("eclipselink.target-database", targetDatabase);

jpaProperties.put("eclipselink.logging.level", "FINE");

jpaProperties.put("javax.persistence.jdbc.user", user);

jpaProperties.put("javax.persistence.jdbc.password", password);

jpaProperties.put("javax.persistence.jdbc.url",url);

jpaProperties.put("javax.persistence.jdbc.driver",driver);

EntityManagerFactory emf = Persistence.createEntityManagerFactory(

"com.bitplan.java8sorting", jpaProperties);

entityManager = emf.createEntityManager();

}

return entityManager;

}

}

/**

* persist the given Folder with the given entityManager

* @param em - the entityManager

* @param folderJpa - the folder to persist

*/

public void persist(EntityManager em, Folder folder) {

em.getTransaction().begin();

em.persist(folder);

em.getTransaction().commit();

}

/**

* check the sorting - assert that the list has the correct size NUM_DOCUMENTS and that documents

* are sorted by name assuming test# to be the name of the documents

* @param sortedDocuments - the documents which should be sorted by name

*/

public void checkSorting(List sortedDocuments) {

assertEquals(NUM_DOCUMENTS,sortedDocuments.size());

for (int i=1;i<=NUM_DOCUMENTS;i++) {

Document document=sortedDocuments.get(i-1);

assertEquals("test"+i,document.getName());

}

}

/**

* this test case shows that the list of documents retrieved will not be sorted if

* JDK8 and lazy fetching is used

*/

@Test

public void testSorting() {

// get a folder with a few documents

Folder folder=Folder.getFolderExample();

// get an entitymanager JPA_DERBY=inMemory JPA_MYSQL=Mysql disk database

EntityManager em=JPA_DERBY.getEntityManager();

// persist the folder

persist(em,folder);

// sort list directly created from memory

checkSorting(folder.getDocumentsByName());

// detach entities;

em.clear();

// get all folders from database

String sql="select f from Folder f";

Query query = em.createQuery(sql);

@SuppressWarnings("unchecked")

List folders = query.getResultList();

// there should be exactly one

assertEquals(1,folders.size());

// get the first folder

Folder folderJPA=folders.get(0);

// sort the documents retrieved

checkSorting(folderJPA.getDocumentsByName());

}

}

最后

以上就是香蕉小猫咪为你收集整理的java8 collections.sort,Java8 Collections.sort(有时)不排序JPA返回的列表的全部内容,希望文章能够帮你解决java8 collections.sort,Java8 Collections.sort(有时)不排序JPA返回的列表所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部