MyBatis returns some columns as null, 0 - spring-boot

I am using MyBatis and Spring Boot. I am trying to extract data from the db using this:
<select id="queryDeviceList" resultType="DeviceList">
SELECT id, mac_address, is_active
FROM ct_device_list dl
where dl.is_active = 1
</select>
and my POJO is
#Data
public class DeviceList {
private int id;
private String mac_address;
private int is_active;
}
and my DAO is
List <DeviceList> queryDeviceList();
and my table CT_DEVICE_LIST in Oracle db has
CREATE TABLE CT_DEVICE_LIST
(
ID NUMBER(10,0) NOT NULL
, MAC_ADDRESS VARCHAR2(17) NOT NULL
, IS_ACTIVE NUMBER(1) NOT NULL
, CREATED_DATE DATE NOT NULL
, CONSTRAINT CT_DEVICE_LIST_PK PRIMARY KEY
(
ID
)
ENABLE
);
But the results I got is:
id=1, mac_address=null, is_active = 0
but my mac_address has value and is_active is not 0 in the database.
Please help. Thanks.

if you set mybatis.configuration.map-underscore-to-camel-case=true, you should use
#Data
public class DeviceList {
private Integer id;
private String macAddress;
private Integer isActive;
}
Remember not to use primitive type int, if you don't want set default to 0.

Related

Strange validation conflict in Spring JPA TableGenerator

I have a legacy database with composite primary key in table project. (BaseEntity contains common properties for lastModifiedDate and lastModifiedBy)
#Entity
#IdClass(ProjectPk.class)
public class Project extends BaseEntity {
#Id
#GeneratedValue(strategy=GenerationType.TABLE, generator="nextProjectId")
#TableGenerator(
name="nextProjectId",
table="projectId",
pkColumnName = "proj_Id",
pkColumnValue="proj_id"
)
private Long projId;
#Id
private int version;
//other properties, getters and setters omitted for clarity
}
PK class
public class ProjectPk implements java.io.Serializable {
private int projId;
private int version;
//both constructoirs, equals, hashcode, getters and setters omitted for clarity
}
I have flyway migration files to simulate production database.
drop table if exists project;
CREATE TABLE project
(
proj_id bigint,
version int,
-- other columns omitted for clarity
PRIMARY KEY (`proj_id`, `version`)
) ENGINE=InnoDB;
drop table if exists project_id;
CREATE TABLE project_id
(
proj_id bigint
) ENGINE=InnoDB;
flyway creates tables as ordered in migration file
Table: project_id
Columns:
proj_id bigint
...
Table: project
Columns:
proj_id bigint PK
version int PK
...
during maven build I'm getting validation error
Schema-validation: wrong column type encountered in column [proj_id] in table [project_id]; found [bigint (Types#BIGINT)], but expecting [varchar(255) (Types#VARCHAR)]
What I did wrong to make hibernate expect [varchar(255) (Types#VARCHAR)]?
This is SpringBoot project 2.6.6 with MySql database
I see the following problems with your code:
Type mismatch between Project.projId (Long type) and ProjectPk.projId (int type).
You use wrong table structure for the project_id table.
You can see a working example below.
Assuming that you have the following tables:
CREATE TABLE test_project
(
proj_id bigint,
version int,
title VARCHAR(50),
PRIMARY KEY (proj_id, version)
);
create table table_identifier (
table_name varchar(255) not null,
product_id bigint,
primary key (table_name)
);
insert into table_identifier values ('test_project', 20);
and the following mapping:
#Entity
#Table(name = "test_project")
#IdClass(ProjectPk.class)
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "nextProjectId")
#TableGenerator(
name="nextProjectId",
table="table_identifier",
pkColumnName = "table_name",
valueColumnName="product_id",
allocationSize = 5
)
#Column(name = "proj_id")
private Long projId;
#Id
private int version;
// other fields, getters, setters ...
}
you will be able to persist the entity like below:
Project project = new Project();
project.setVersion(1);
// ...
entityManager.persist(project);

jpa generated schema doesn't include property of extended class

I've a very complex database which i will try to resume in here
#Embeddable
open class ChargeableDTO(
#NotBlank var name: String,
#NotBlank var ref: String,
#Min(1) var priceCents: Int,
#NotNull #Min(1) #Max(12) var maxInstallments: Int = 1,
#NotNull var gateway: PaymentGateway) {
#Embeddable
class CreditPackageDTO(name: String,
ref: String,
priceCents: Int,
maxInstallments: Int = 1,
gateway: PaymentGateway,
#Min(1) var creditAmount : Int) : ChargeableDTO(name, ref, priceCents, maxInstallments, gateway) {
#Entity
#Table(name = "credit_packages", uniqueConstraints = [UniqueConstraint(columnNames = ["gateway", "ref"])])
class CreditPackage(dto: CreditPackageDTO) : ChargeableEntity(dto)
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class ChargeableEntity(#field:Embedded open var dto: ChargeableDTO) : HydraEntity()
many other classes that are not relevant to this problem....
but when running the schema generation script hibernate generates a code like
create table credit_packages (
id bigint not null,
created_date datetime(6),
last_modified_date datetime(6),
public_id varchar(255),
gateway integer,
max_installments integer not null,
name varchar(255),
price_cents integer not null,
ref varchar(255),
primary key (id)
) engine=InnoDB
the first 4 fields come from a parent class which all my entities inherit from.
but this schema complete ignores the property creditAmount which is defined in the extended dto
also this code doesn't metion the limit 1 to 12 for maxinstallments
am i doing anything wrong, how can i fix it?

Why doesn't Mybatis map a simple ENUM correctly?

I'm not doing anything out of the ordinary from what I can tell. I have a spring boot application using mybatis:
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1'
I have an application.properties config for mybatis that is pretty simple:
## MyBatis ##
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.default-statement-timeout=30
My database table looks like this:
CREATE TABLE workspace_external_references (
id CHAR(36) PRIMARY KEY,
workspace_id CHAR(36) NOT NULL,
site VARCHAR(255) NOT NULL,
external_id VARCHAR(255) NOT NULL,
created_at DATETIME(6) NOT NULL DEFAULT NOW(6),
updated_at DATETIME(6) NOT NULL DEFAULT NOW(6),
FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE
)
With just a single entry like this:
'a907c0af-216a-41e0-b16d-42107a7af05f', 'e99e4ab4-839e-405a-982b-08e00fbfb2d4', 'ABC', '6', '2020-06-09 00:19:20.135822', '2020-06-09 00:19:20.135822'
In my mapper file I'm doing a select of all references like this:
#Select("SELECT * FROM workspace_external_references WHERE workspace_id = #{workspaceId}")
List<WorkspaceExternalReference> findByWorkspace(#Param("workspaceId") final UUID workspaceId);
And the java object that this is supposed to map to looks like this:
public class WorkspaceExternalReference {
private UUID id;
private UUID workspaceId;
private Sites site;
private String externalId;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public WorkspaceExternalReference(
final Sites site,
final UUID workspaceId,
final String externalId) {
this.site = site;
this.workspaceId = workspaceId;
this.externalId = externalId;
}
}
public enum Sites {
ABC, XYZ;
}
Sooooo why doesn't this work? I get this error back:
Caused by: org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'id' from result set. Cause: java.lang.IllegalArgumentException: No enum constant com.acme.Sites.a907c0af-216a-41e0-b16d-42107a7af05f
When there is no default constructor, you need to let MyBatis know which columns to pass to the constructor explicitly (in most cases).
With annotations, it would look as follows.
You can use <resultMap> and <constructor> in XML mapper.
#ConstructorArgs({
#Arg(column = "site", javaType = Sites.class),
#Arg(column = "workspace_id", javaType = UUID.class),
#Arg(column = "external_id", javaType = String.class)
})
#Select("SELECT * FROM workspace_external_references WHERE workspace_id = #{workspaceId}")
List<WorkspaceExternalReference> findByWorkspace(#Param("workspaceId") final UUID workspaceId);
Other columns (i.e. id, created_at, updated_at) will be auto-mapped via setters (if there are) or reflection.
Alternatively, you can just add the default (no-arg) constructor to the WorkspaceExternalReference class. Then all columns will be auto-mapped after the class is instantiated.
Note: To make it work, there needs to be a type handler registered for UUID, but you seem to have done it already (otherwise the parameter mapping wouldn't work).

Entity primary key and sequence

i use oracle and openjpa.
i have an primary key and i want to use a sequence for its value
CREATE TABLE LOG (
ID NUMBER(10) not null,
TIMESTAMP TIMESTAMP DEFAULT (SYSDATE),
constraint PK_ID PRIMARY KEY (ID)
);
CREATE SEQUENCE ID_SEQ
START WITH 1
INCREMENT BY 1
NOCACHE
NOCYCLE;
#Entity
#Table(name="Log")
public class Log implements Serializable {
#Id
#SequenceGenerator(name="SEQ_GEN", sequenceName="ID_SEQ", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN" )
#Column(name="id",nullable=false)
private long id;
#Column(name="timestamp",nullable=false)
private Timestamp timestamp;
public Log(){
}
public Log(Timestamp timestamp){
this.timestamp = timestamp;
}
..
}
#Stateless
public class LogDAO {
#PersistenceContext(unitName="logEntityPU")
private EntityManager em ;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public boolean insert(Log log){
em.persist(log);
return true;
}
...
...
}
when i check my Log object, id=0 and the timestamp is ok
but i get this error
ORA-01400: integrity constraint violation: NOT NULL check constraint.Insertion of null value not allowed
it's like jpa don't do the link with the sequence.
when i debug more, i see this error: javax.persistence.TransactionRequiredException: No active transaction for PuId=
any idea?
A trigger like this will insert the primary key if one is not provided by the app
CREATE OR REPLACE TRIGGER YOUR_SCHEMA.TRG_YOUR_TABLE
BEFORE INSERT
ON YOUR_SCHEMA.YOUR_TABLE
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
PrimaryKeyId NUMBER;
BEGIN
IF :NEW.PRIMARY_KEY_ID IS NULL THEN
SELECT your_schema.seq_your_table.nextval
INTO :NEW.PRIMARY_KEY_ID
FROM dual;
END IF;
END YOUR_SCHEMA.TRG_YOUR_TABLE;
Then do a
Select YOUR_SCHEMA.seq_your_table.currval from dual;
to get the value

Hibernate MapKeyManyToMany gives composite key where none exists

I have a Hibernate (3.3.1) mapping of a map using a three-way join table:
#Entity
public class SiteConfiguration extends ConfigurationSet {
#ManyToMany
#MapKeyManyToMany(joinColumns=#JoinColumn(name="SiteTypeInstallationId"))
#JoinTable(
name="SiteConfig_InstConfig",
joinColumns = #JoinColumn(name="SiteConfigId"),
inverseJoinColumns = #JoinColumn(name="InstallationConfigId")
)
Map<SiteTypeInstallation, InstallationConfiguration>
installationConfigurations = new HashMap<SiteTypeInstallation, InstallationConfiguration>();
...
}
The underlying table (in Oracle 11g) is:
Name Null Type
------------------------------ -------- ----------
SITECONFIGID NOT NULL NUMBER(19)
SITETYPEINSTALLATIONID NOT NULL NUMBER(19)
INSTALLATIONCONFIGID NOT NULL NUMBER(19)
The key entity used to have a three-column primary key in the database, but is now redefined as:
#Entity
public class SiteTypeInstallation implements IdResolvable {
#Id
#GeneratedValue(generator="SiteTypeInstallationSeq", strategy= GenerationType.SEQUENCE)
#SequenceGenerator(name = "SiteTypeInstallationSeq", sequenceName = "SEQ_SiteTypeInstallation", allocationSize = 1)
long id;
#ManyToOne
#JoinColumn(name="SiteTypeId")
SiteType siteType;
#ManyToOne
#JoinColumn(name="InstalationRoleId")
InstallationRole role;
#ManyToOne
#JoinColumn(name="InstallationTypeId")
InstType type;
...
}
The table for this has a primary key 'Id' and foreign key constraints+indexes for each of the other columns:
Name Null Type
------------------------------ -------- ----------
SITETYPEID NOT NULL NUMBER(19)
INSTALLATIONROLEID NOT NULL NUMBER(19)
INSTALLATIONTYPEID NOT NULL NUMBER(19)
ID NOT NULL NUMBER(19)
For some reason, Hibernate thinks the key of the map is composite, even though it isn't, and gives me this error:
org.hibernate.MappingException: Foreign key (FK1A241BE195C69C8:SiteConfig_InstConfig [SiteTypeInstallationId])) must have same number of columns as the referenced primary key (SiteTypeInstallation [SiteTypeId,InstallationRoleId])
If I remove the annotations on installationConfigurations and make it transient, the error disappears.
I am very confused why it thinks SiteTypeInstallation has a composite key at all when #Id is clearly defining a simple key, and doubly confused why it picks exactly just those two columns. Any idea why this happens? Is it possible that JBoss (5.0 EAP) + Hibernate somehow remembers a mistaken idea of the primary key across server restarts and code redeployments?
Thanks in advance,
-Lars

Resources