I have two entities:
#Indexed
#Entity
#Table(name = "LK_CONTACT_TYPE")
public class ContactTypeEntity {
#Id
#Column(name = "ID")
#DocumentId
Integer id;
#SortableField
#Field(store = Store.YES, bridge = #FieldBridge(impl = ContactTypeComparator.class))
#Column(name = "NAME")
String name;
getter() .. setter()..
}
#Indexed
#Entity
#Table(name = "DIRECTORY")
public class DirectoryEntity {
....
#IndexedEmbedded(prefix = "contactType.", includePaths = {"id", "name"})
#ManyToOne
#JoinColumn(name = "CONTACT_TYPE")
private ContactTypeEntity contactType;
getter() ... setter()...
}
public class ContactTypeComparator implements MetadataProvidingFieldBridge, TwoWayStringBridge {
#Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
if ( value != null ) {
int ordinal = getOrdinal(value.toString());
luceneOptions.addNumericFieldToDocument(name, ordinal, document);
}
}
#Override
public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
builder.field(name, FieldType.INTEGER).sortable(true);
}
private int getOrdinal(ContactType value) {
switch( value ) {
case PBX: return 0;
case TEL: return 1;
case GSM: return 2;
case FAX: return 3;
default: return 4;
}
}
#Override
public Object get(String name, Document document) {
return document.get( name );
}
#Override
public String objectToString(Object object) {
return object.toString();
}
}
and Query part:
...
query.setSort(queryBuilder.sort().byScore().andByField("contactType.name").createSort());
query.setProjection(... , "contactType.name",...);
...
I am getting the following error:
java.lang.IllegalStateException: unexpected docvalues type NONE for field 'contactType.name' (expected=NUMERIC). Use UninvertingReader or index with docvalues.
Note: I am using hibernate-search 5.10.
I want to show contactType.name name on UI instead of number.
For more detail
Seems my original suggestion was missing a bit in the set() method, in order to add the docvalues:
#Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
if ( value != null ) {
int ordinal = getOrdinal(value.toString());
luceneOptions.addNumericFieldToDocument(name, ordinal, document);
// ADD THIS
luceneOptions.addNumericDocValuesFieldToDocument(name, ordinal, document);
}
}
On top of that, if you need to use the field for both sort and projection, I would recommend declaring two fields. Otherwise the projection will return integers, which is not what you want.
So, do this:
#Indexed
#Entity
#Table(name = "LK_CONTACT_TYPE")
public class ContactTypeEntity {
#Id
#Column(name = "ID")
#DocumentId
Integer id;
#SortableField
// CHANGE THESE TWO LINES
#Field(store = Store.YES)
#Field(name = "name_sort", bridge = #FieldBridge(impl = ContactTypeComparator.class))
#Column(name = "NAME")
String name;
getter() .. setter()..
}
#Indexed
#Entity
#Table(name = "DIRECTORY")
public class DirectoryEntity {
....
// CHANGE THIS LINE
#IndexedEmbedded(prefix = "contactType.", includePaths = {"id", "name", "name_sort"})
#ManyToOne
#JoinColumn(name = "CONTACT_TYPE")
private ContactTypeEntity contactType;
getter() ... setter()...
}
public class ContactTypeComparator implements MetadataProvidingFieldBridge, TwoWayStringBridge {
#Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
if ( value != null ) {
int ordinal = getOrdinal(value.toString());
luceneOptions.addNumericFieldToDocument(name, ordinal, document);
// ADD THIS LINE
luceneOptions.addNumericDocValuesFieldToDocument(name, ordinal, document);
}
}
#Override
public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
builder.field(name, FieldType.INTEGER).sortable(true);
}
private int getOrdinal(ContactType value) {
switch( value ) {
case PBX: return 0;
case TEL: return 1;
case GSM: return 2;
case FAX: return 3;
default: return 4;
}
}
#Override
public Object get(String name, Document document) {
return document.get( name );
}
#Override
public String objectToString(Object object) {
return object.toString();
}
}
Then query like this:
...
query.setSort(queryBuilder.sort().byScore().andByField("contactType.name_sort").createSort());
query.setProjection(... , "contactType.name",...);
...
Related
In a JavaEE JPA web application, Feature entity has bidirectional ManyToOne relationship with Patient Entity. I want to write a query to count the number of Patients who have one or more matching criteria features. I use EclipseLink as the Persistence Provider.
For example, I want to count the number of patients who have a feature with 'variableName' = 'Sex' and 'variableData' = 'Female' and another feature with 'variableName' = 'smoking' and 'variableData' = 'yes'.
How can I write a JPQL query to get the count of patients?
After the first answer, I tried this Query does not give any results as expected.
public void querySmokingFemales(){
String j = "select count(f.patient) from Feature f "
+ "where ((f.variableName=:name1 and f.variableData=:data1)"
+ " and "
+ " (f.variableName=:name2 and f.variableData=:data2))";
Map m = new HashMap();
m.put("name1", "sex");
m.put("data1", "female");
m.put("name2", "smoking");
m.put("data2", "yes");
count = getFacade().countByJpql(j, m);
}
The Patient entity is as follows.
#Entity
public class Patient implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy = "patient")
private List<Feature> features;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Patient)) {
return false;
}
Patient other = (Patient) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "entity.Patient[ id=" + id + " ]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Feature> getFeatures() {
return features;
}
public void setFeatures(List<Feature> features) {
this.features = features;
}
}
This is the Feature Entity.
#Entity
public class Feature implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String variableName;
private String variableData;
#ManyToOne
private Patient patient;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Feature)) {
return false;
}
Feature other = (Feature) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "entity.Feature[ id=" + id + " ]";
}
public String getVariableName() {
return variableName;
}
public void setVariableName(String variableName) {
this.variableName = variableName;
}
public String getVariableData() {
return variableData;
}
public void setVariableData(String variableData) {
this.variableData = variableData;
}
public Patient getPatient() {
return patient;
}
public void setPatient(Patient patient) {
this.patient = patient;
}
}
For single feature counts you can use this
select count(f.patient) from Feature f where f.variableName=:name and f.variableData:=data
Two feature counts
select count(distinct p) from Patient p, Feature f1, Feature f2
where
p.id=f1.patient.id and p.id=f2.patient.id and
f1.variableName=:name1 and f1.variableData:=data1 and
f2.variableName=:name2 and f2.variableData:=data2
Multiple feature counts solution is a bit tricky. org.springframework.data.jpa.domain.Specification can be used
public class PatientSpecifications {
public static Specification<Patient> hasVariable(String name, String data) {
return (root, query, builder) -> {
Subquery<Fearure> subquery = query.subquery(Fearure.class);
Root<Fearure> feature = subquery.from(Fearure.class);
Predicate predicate1 = builder.equal(feature.get("patient").get("id"), root.get("id"));
Predicate predicate2 = builder.equal(feature.get("variableName"), name);
Predicate predicate3 = builder.equal(feature.get("variableData"), data);
subquery.select(operation).where(predicate1, predicate2, predicate3);
return builder.exists(subquery);
}
}
}
Then your PatientRepository have to extend org.springframework.data.jpa.repository.JpaSpecificationExecutor<Patient>
#Repository
public interface PatientRepository
extends JpaRepository<Patient, Long>, JpaSpecificationExecutor<Patient> {
}
Your service method:
#Service
public class PatientService {
#Autowired
PatientRepository patientRepository;
//The larger map is, the more subqueries query would involve. Try to avoid large map
public long countPatiens(Map<String, String> nameDataMap) {
Specification<Patient> spec = null;
for(Map.Entry<String, String> entry : nameDataMap.entrySet()) {
Specification<Patient> tempSpec = PatientSpecifications.hasVariable(entry.getKey(), entry.getValue());
if(spec != null)
spec = Specifications.where(spec).and(tempSpec);
else spec = tempSpec;
}
Objects.requireNonNull(spec);
return patientRepository.count(spec);
}
}
We also handled same situation for two feature and after extracting the IDs, we used a nested loops after and counting the number of common count. It was resource intensive and this two feature query in the answer helped a lot.
May need to redesign the Class Structure so that querying is easier.
I have 3 Entities Parent,Child,SubChild. Parent is a parent of Child and Child is a parent of SubChild. I need to insert around 700 objects of Parent. Parent can have 50 Objects of Child. Child can have 50 objects of SubChild.
I tried normal repository.save(ListOfObjects) it takes approx 4mins.
Then I tried using entity manager's persist, flush and clear based on batch size(500). This also took approx 4 mins.
There wasn't much difference in performance. Please suggest a best way to insert such a high amount of data efficiently.
Parent
#Entity
public class Parent {
#Id #GeneratedValue(strategy= GenerationType.AUTO)
private Long parentId;
private String aaa;
private String bbb;
private String ccc;
#Version
private Long version;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "parent", fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column=#JoinColumn(name="parentId",referencedColumnName="parentId",nullable=false))})
private List<Child> childs = new ArrayList<>();
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getAaa() {
return aaa;
}
public void setAaa(String aaa) {
this.aaa = aaa;
}
public String getBbb() {
return bbb;
}
public void setBbb(String bbb) {
this.bbb = bbb;
}
public String getCcc() {
return ccc;
}
public void setCcc(String ccc) {
this.ccc = ccc;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public List<Child> getChilds() {
return childs;
}
public void setChilds(List<Child> childs) {
this.childs = childs;
}
}
Child
#Entity
public class Child {
#Id #GeneratedValue(strategy= GenerationType.AUTO)
private Long childId;
private String ddd;
private String ccc;
private Integer eee;
#OneToMany(cascade = CascadeType.ALL,orphanRemoval = true, mappedBy = "child", fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column = #JoinColumn(name = "childId", referencedColumnName = "childId", nullable = false)) })
private List<SubChild> subChilds = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column= #JoinColumn( name="parentId",referencedColumnName="parentId",nullable=false))
})
private Parent parent;
public Long getChildId() {
return childId;
}
public void setChildId(Long childId) {
this.childId = childId;
}
public String getDdd() {
return ddd;
}
public void setDdd(String ddd) {
this.ddd = ddd;
}
public String getCcc() {
return ccc;
}
public void setCcc(String ccc) {
this.ccc = ccc;
}
public Integer getEee() {
return eee;
}
public void setEee(Integer eee) {
this.eee = eee;
}
public List<SubChild> getSubChilds() {
return subChilds;
}
public void setSubChilds(List<SubChild> subChilds) {
this.subChilds = subChilds;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
SubChild
#Entity
public class SubChild {
#Id #GeneratedValue(strategy= GenerationType.AUTO)
private Long subChildId;
private String fff;
private String ggg;
private Integer hhh;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column= #JoinColumn( name="childId",referencedColumnName="childId",nullable=false))
})
private Child child;
public Long getSubChildId() {
return subChildId;
}
public void setSubChildId(Long subChildId) {
this.subChildId = subChildId;
}
public String getFff() {
return fff;
}
public void setFff(String fff) {
this.fff = fff;
}
public String getGgg() {
return ggg;
}
public void setGgg(String ggg) {
this.ggg = ggg;
}
public Integer getHhh() {
return hhh;
}
public void setHhh(Integer hhh) {
this.hhh = hhh;
}
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
}
Repository method used for persisting the list of Parent Entity
#Value("${spring.jpa.hibernate.jdbc.batch_size}")
private int batchSize;
public <T extends Parent> Collection<T> bulkSave(Collection<T> entities) {
final List<T> savedEntities = new ArrayList<T>(entities.size());
int i = 0;
for (T t : entities) {
savedEntities.add(persistOrMerge(t));
i++;
if (i % batchSize == 0) {
// Flush a batch of inserts and release memory.
entityManager.flush();
entityManager.clear();
}
}
return savedEntities;
}
private <T extends Parent> T persistOrMerge(T t) {
if (t.getTimeSlotId() == null) {
entityManager.persist(t);
return t;
} else {
return entityManager.merge(t);
}
}
application.yml
spring:
application:
name: sample-service
jpa:
database: MYSQL
show-sql: true
hibernate:
ddl-auto: update
dialect: org.hibernate.dialect.MySQL5Dialect
naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
jdbc:
batch_size: 100
jackson:
date-format: dd/MM/yyyy
thymeleaf:
cache: false
spring.datasource.url : jdbc:mysql://${dbhost}/sample?createDatabaseIfNotExist=true
spring.datasource.username : root
spring.datasource.password : root
spring.datasource.driver-class-name : com.mysql.cj.jdbc.Driver
To enable batch insert you need the batch_size property which you have in your configuration.
Also since a jdbc batch can target one table only you need the spring.jpa.hibernate.order_inserts=true property to order the insert between parent and child or else the statement are unordered and you will see a partial batch (new batch anytime an insert in a different table is called)
i have two tables:
area (
id int PK autoincrement
code varchar
)
products (
id int PK autoincrement
area_id int
)
And the objets are defined like this:
class Product {
...
#JoinColumn(name = "area_id", referencedColumnName = "id")
#ManyToOne
#Expose
private Area area;
...
}
This works fine but I want that area to be a String with the code used in the table area column code.
class Product {
...
???
private String area;
...
}
What should be the annotations to make this work?
Thanks!
Try to use a combination of #SecondaryTable and #Column annotations. Something like this:
#Entity
#SecondaryTable(name="area", pkJoinColumns=#PrimaryKeyJoinColumn(name="id", referencedColumnName="area_id"))
class Product {
...
#Column(name="code", table = "area")
private String code;
...
}
If there is some poor soul with the same problem, here is how I did it:
Using transformers. So the field area is defined like this:
#Transformation(fetch = FetchType.EAGER, optional = false)
#ReadTransformer(transformerClass = AreaAttributeTransformer.class)
#WriteTransformers({
#WriteTransformer(
transformerClass = AreaFieldTransformer.class,
column = #Column(name = "area_id", nullable = false))
})
#Expose
private String area;
Then those clases work like this:
AreaAttributeTransformer
public class AreaAttributeTransformer implements AttributeTransformer {
private AbstractTransformationMapping mapping;
#Override
public void initialize(AbstractTransformationMapping abstractTransformationMapping) {
this.mapping = abstractTransformationMapping;
}
#Override
public Object buildAttributeValue(Record record, Object o, Session session) {
for (DatabaseField field : mapping.getFields()) {
if (field.getName().contains("area_id")) {
EntityManager em = MyEntityManagerFactory.getENTITY_MANAGER_FACTORY().createEntityManager();
List results = em.createNamedQuery("Areas.findById")
.setParameter("id", record.get(field))
.getResultList();
if (results.size() > 0)
return ((Area) results.get(0)).getCode();
}
}
return null;
}
}
AreaFieldTransformer
public class AreaFieldTransformer implements FieldTransformer {
private AbstractTransformationMapping mapping;
#Override
public void initialize(AbstractTransformationMapping abstractTransformationMapping) {
this.mapping = abstractTransformationMapping;
}
#Override
public Object buildFieldValue(Object o, String s, Session session) {
if (o instanceof RouSub) {
EntityManager em = MyEntityManagerFactory.getENTITY_MANAGER_FACTORY().createEntityManager();
List results = em.createNamedQuery("Area.findByCode")
.setParameter("area", ((Area) o).getCode())
.getResultList();
if (results.size() > 0)
return ((Area)results.get(0)).getId();
}
return null;
}
}
I have two entities - Group and UserGroup, they are connected with groupId.
"\" are because postgre is case sensitive and this way we correct this fact.
#Entity
#Table(name = "\"Group\"")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "\"groupId\"")
private int groupId;
#Column(name = "\"groupName\"")
private String groupName;
#OneToMany(mappedBy = "group")
List<Project> projects;
#OneToMany(mappedBy = "group")
private List<UserGroup> members;
public Group(String groupName) {
this.groupName = groupName;
}
public Group() {
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public List<Project> getProjects() {
return projects;
}
public void setProjects(List<Project> projects) {
this.projects = projects;
}
public List<UserGroup> getMembers() {
return members;
}
public void setMembers(List<UserGroup> members) {
this.members = members;
}
#Override
public String toString() {
return "Group{" +
"groupId=" + groupId +
", groupName='" + groupName + '\'' +
'}';
}
}
And UserGroup
#Entity
#Table(name = "\"UserGroup\"")
#IdClass(GroupAssociationId.class)
public class UserGroup {
#Id
#Column(name = "\"userId\"")
private int userId;
#Id
#Column(name = "\"groupId\"")
private int groupId;
#ManyToOne
#PrimaryKeyJoinColumn(name = "\"userId\"", referencedColumnName = "\"userId\"")
private User member;
#ManyToOne
#PrimaryKeyJoinColumn(name = "\"groupId\"", referencedColumnName = "\"groupId\"")
private Group group;
#ManyToOne
#JoinColumn(name = "\"accessId\"")
private Access access;
public UserGroup(Group group, User member, Access access) {
this.group = group;
this.member = member;
this.access = access;
}
public UserGroup() {
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public User getMember() {
return member;
}
public void setMember(User member) {
this.member = member;
}
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
public Access getAccess() {
return access;
}
public void setAccess(Access access) {
this.access = access;
}
#Override
public String toString() {
return "UserGroup{" +
"userId=" + userId +
", groupId=" + groupId +
", access=" + access.getAccessName() +
'}';
}
}
When I try to create a row in a table UserGroup I get a mistake:
Caused by: org.postgresql.util.PSQLException: ERROR: column "group_groupId" of relation "UserGroup" does not exist
Why? This happens on the string "em.getTransaction().commit(). It is really strange.
In the table UserGroup, a column:
"group_`groupId`"
was generated (because you are using "" to preserve case sensitive.
You can edit in postgres the name for the column (and the foreing key too):
"group_`groupId`" ---> "group_groupId"
JPA is looking for group_groupId.
I've managed to answer this question. The problem was in sequence generation. When generating in embedded database, I don't know why, the generation type sequence doesn't work. Instead I used Identity type and everything started working
I'm trying to do a database lookup using JPA with EclipseLink. My database is Oracle 11.2.0. I have the following entity classes defined:
#Entity
#Table(name = "BS_PROVIDERS")
public class BsProvider {
#Id
#Column(name = "GUID")
private String guid;
public String getGuid() { return guid; }
public void setGuid(String guid) { this.guid = guid; }
#Column(name = "NAME")
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
#Entity
#Table(name = "CP_PROVIDERS")
public class CpProvider{
#Id
#Column(name = "GUID")
private String guid;
public String getGuid() { return guid; }
public void setGuid(String guid) { this.guid = guid; }
#Column(name = "NAME")
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
#NamedQueries({
#NamedQuery(
name = Catalog.FIND_BY_CPP_AND_BSP,
query = "select c from Catalog c where (c.cpProvider = :cpProvider) and ( (:bsProvider IS NULL) or (c.bsProvider = :bsProvider))"
)
})
#Entity
#Table(name = "CATALOGS")
public class Catalog {
public static final String FIND_BY_CPP_AND_BSP = "Catalog.findByCppAndBsp";
public static final String CP_PROVIDER_PARAM = "cpProvider";
public static final String BS_PROVIDER_PARAM = "bsProvider";
#Id
#Column(name = "GUID")
private String guid;
public String getGuid() { return guid; }
public void setGuid(String guid) { this.guid = guid; }
#Column(name = "NAME")
private String name;
public String getName() { return name; }
public void setName() { this.name = name; }
#ManyToOne
#JoinColumn(name = "CPP_GUID")
private CpProvider cpProvider;
public CpProvider getCpProvider() { return cpProvider; }
public void setCpProvider(CpProvider cpProvider) { this.cpProvider = cpProvider; }
#ManyToOne()
#JoinColumn(name = "BSP_GUIG")
private BsProvider bsProvider;
public BsProvider getBsProvider() { return bsProvider; }
public void setBsProvider(BsProvider bsProvider) { this.bsProvider = bsProvider; }
}
Code that creates the query and sets the parameters:
TypedQuery<Catalog> catalogQuery = em.createNamedQuery(Catalog.FIND_BY_CPP_AND_BSP, Catalog.class);
catalogQuery.setParameter(Catalog.CP_PROVIDER_PARAM, cpProvider);
catalogQuery.setParameter(Catalog.BS_PROVIDER_PARAM, bsProvider);
List<Catalog> catalogList = catalogQuery.getResultList();
When the variable bsProvider is NULL, all parameters are registerd are registerd correctly according to the EclipseLink log:
SELECT GUID, NAME, BSP_GUIG, CPP_GUID FROM CATALOGS WHERE ((CPP_GUID = ?) AND ((? IS NULL) OR (BSP_GUIG = ?)))
bind => [18EC0EDB-21A4-4845-960A-D5D2BDAC7B87, null, null]
Otherwise when the variable bsProvider refers to an existing entity I get the following exception:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Invalid column type
Error Code: 17004
Call: SELECT GUID, NAME, BSP_GUIG, CPP_GUID FROM CATALOGS WHERE ((CPP_GUID = ?) AND ((? IS NULL) OR (BSP_GUIG = ?)))
bind => [18EC0EDB-21A4-4845-960A-D5D2BDAC7B87, com.bssys.ebpp.dbaccess.model.BsProvider#5dbbe8df, 44E8F4BF-CFDC-49DB-AB0B-718C72D6B4EF]
As you can see the first and the third parameters are bound correctly (they are replaced by the primary key values), but the second is not. What's the reason of such a strange behavior?