Hibernate | Mapping data type "byte" with enum attribute converter not working - error: No Dialect mapping for JDBC type: 277630005 - spring

Using spring boot version: 2.2.6.RELEASE | Database: SqlServer 2008 | org.hibernate.dialect.SQLServer2008Dialect.
When using a converter for mapping the enumeration of a column of the type "TINYINT" that would be referring to "Byte" in java is presenting the following error:
No Dialect mapping for JDBC type: 277630005, when hibernate ddl for initial db.
However, when doing the direct mapping as Byte without using enumeration, it works correctly.
Converter:
#Converter
public class CharacterClassConverter implements AttributeConverter<CharacterClassIndicator, Byte> {
#Override
public Byte convertToDatabaseColumn(CharacterClassIndicator characterClassIndicator) {
return characterClassIndicator != null ? characterClassIndicator.getCode() : null;
}
#Override
public CharacterClassIndicator convertToEntityAttribute(Byte code) {
return Stream.of(CharacterClassIndicator.values())
.filter(value -> value.getCode().equals(code))
.findFirst()
.orElseThrow(UnsupportedOperationException::new);
}
}
Enum:
public enum CharacterClassIndicator {
DARK_WIZARD(0, "DK", "Dark Wizard"),
SOUL_MASTER(1, "SM", "Soul Master"),
GRAND_MASTER(2, "GM", "Grand Master"),
DARK_KNIGHT(16, "DK", "Dark Knight"),
BLADE_KNIGHT(17, "BK", "Blade Knight"),
BLADE_MASTER(18, "BM", "Blade Master"),
FAIRY_ELF(32, "FE", "Fairy Elf"),
MOUSE_ELF(33, "ME", "Mouse Elf"),
HIGH_ELF(34, "HE", "High Elf"),
DARK_LORD(48, "DL", "Dark Lord"),
LORD_EMPEROR(49, "LE", "Lord Emperor"),
MAGIC_GLADIATOR(64, "MG", "Magic Gladiator"),
DUEL_MASTER(65, "DM", "Duel Master");
private final Byte code;
private final String initials;
private final String name;
CharacterClassIndicator(Integer code, String initials, String name) {
this.code = code.byteValue();
this.initials = initials;
this.name = name;
}
public Byte getCode() {
return code;
}
public String getInitials() {
return initials;
}
public String getName() {
return name;
}
}
The entity code:
#Entity
#Table(name = "Character")
public class Character {
#Id
#Column(name = "AccountID")
private String id;
#Column(name = "Name")
private String name;
#Column(name = "Class")
#Convert(converter = CharacterClassConverter.class) // if remove this line and change type to Byte, works correctly
private CharacterClassIndicator characterClass;
#Column(name = "cLevel")
private Integer level;
#Column(name = "Strength")
private Short strenght;
#Column(name = "Dexterity")
private Short dexterity;
#Column(name = "Vitality")
private Short vitality;
#Column(name = "Energy")
private Short energy;
#Column(name = "Resets")
private Integer resets;
.... gets and setters
}
Would anyone have an idea how to solve?

Use columnDefinition
#Column(name = "Class", columnDefinition = "TINYINT")

Related

MapStruct - mapping method from iterable to non-iterable

I have been working with MapStruct some days now and haven't yet achieved what i need.
As part of the exercises with Spring, I am writing a small app that will display information about the movies (title, description, director, etc.) and additionally the movie category.
Therefore, I created an additional Entity called Category, so that (e.g. an admin) could add or remove individual category names.
Movie Entity:
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
private String director;
private int year;
#ManyToMany
#Column(nullable = false)
private List<Category> category;
private LocalDate createdAt;
}
Category Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String categoryName;
private LocalDate createdAt;
}
I packed it all into MapStruct and DTOs.
MovieDTORequest.java
public class MovieDTORequest {
private String title;
private String content;
private String director;
private List<Category> category;
private int year;
}
MovieDTOResponse.java
public class MovieDTOResponse {
private String title;
private String content;
private String director;
private String categoryName;
private int year;
private LocalDate createdAt;
}
And MovieMapper.java
#Mapper(componentModel = "spring")
public interface MovieMapper {
#Mapping(target = "categoryName", source = "category")
MovieDTOResponse movieToMovieDTO(Movie movie);
#Mapping(target = "id", source = "title")
#Mapping(target = "createdAt", constant = "")
Movie movieRequestToMovie(MovieDTORequest request);
#Mapping(target = "id", source = "title")
#Mapping(target = "createdAt", constant = "")
void updateMovie(MovieDTORequest request, #MappingTarget Movie target);
String map(List<Category> value);
}
However, I have a problem with Mapper. First, I got the error:
"Can't map property "List<Category> category" to "String categoryName". Consider to declare/implement a mapping method: "String map(List<Category> value)"
and when I wrote it in Mapper, I have one more error:
Can't generate mapping method from iterable type from java stdlib to non-iterable type.
I am asking for help, because I am already lost.
You should define default implementation for String map(List<Category> value) inside MovieMapper interface, what would Mapstruct use to map property List<Category> category to String categoryName. For example:
#Mapper(componentModel = "spring")
public interface MovieMapper {
#Mapping(target = "categoryName", source = "category")
MovieDTOResponse movieToMovieDTO(Movie movie);
default String map(List<Category> value){
//TODO: Implement your own logic that determines categoryName
return "Movie Categories";
}
}

Getting ConstraintViolationException while saving a row with embedded key in the table with many-to-many mapping between two entities using Spring JPA

In our spring boot Restful WebService, we have two master tables with many-to-many relationship between them. But in the transaction table, we want one extra field (current_time) as part of the embedded key other than the primary keys of the two tables. Now, we’ve created a separate class for defining embedded primary key using #Embeddable. Now, while inserting one transaction row to transaction table using Spring JPA, I am manually setting the primary keys in the corresponding entity and calling the save method on corresponding repository. But It is giving me ConstraintViolationException as the current_time is going with null value even if I have manually set it. Any help would be highly appreciated.
First Entity is as follows :
#Entity
#Table(name = "project")
public class Project {
#Id
#GenericGenerator(name = "projectid", strategy = "com.sample.upload.entity.ProjectIDGenerator")
#GeneratedValue(generator = "projectid")
#Column(name = "projectid")
private String projectID;
#Column(name = "project_name")
private String projectName;
#Column(name = "project_descr")
private String projectDesc;
#Column(name = "project_input_path")
private String projectPath;
#Column(name = "project_creation_time")
private Calendar projectCreationTime;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "project_migration", joinColumns = #JoinColumn(name = "projectid", referencedColumnName = "projectid"), inverseJoinColumns = #JoinColumn(name = "migratorid", referencedColumnName = "migratorid"))
private List<Migrator> migrators;
#Column(name = "account_name")
private String accountName;
#Column(name = "account_group")
private String accountGroup;
public String getProjectID() {
return projectID;
}
public void setProjectID(String projectID) {
this.projectID = projectID;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getAccountGroup() {
return accountGroup;
}
public void setAccountGroup(String accountGroup) {
this.accountGroup = accountGroup;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getProjectDesc() {
return projectDesc;
}
public void setProjectDesc(String projectDesc) {
this.projectDesc = projectDesc;
}
public String getProjectPath() {
return projectPath;
}
public void setProjectPath(String projectPath) {
this.projectPath = projectPath;
}
public Calendar getProjectCreationTime() {
return projectCreationTime;
}
public void setProjectCreationTime(Calendar projectCreationTime) {
this.projectCreationTime = projectCreationTime;
}
public List<Migrator> getMigrators() {
return migrators;
}
public void setMigrators(List<Migrator> migrators) {
this.migrators = migrators;
}
}
Second Entity :
#Entity
#GenericGenerator(name = "generatorName", strategy = "increment")
#Table(name = "migrator")
public class Migrator {
#Id
#GeneratedValue(generator = "generatorName")
#Column(name = "migratorid")
private String migratorId;
#Column(name = "src_tech_name")
private String srcTechName;
#Column(name = "dest_tech_name")
private String destTechName;
#Column(name = "migrator_name")
private String migratorName;
#Column(name = "migrator_type")
private String migratorType;
public String getMigratorId() {
return migratorId;
}
public void setMigratorId(String migratorId) {
this.migratorId = migratorId;
}
public String getSrcTechName() {
return srcTechName;
}
public void setSrcTechName(String srcTechName) {
this.srcTechName = srcTechName;
}
public String getDestTechName() {
return destTechName;
}
public void setDestTechName(String destTechName) {
this.destTechName = destTechName;
}
public String getMigratorName() {
return migratorName;
}
public void setMigratorName(String migratorName) {
this.migratorName = migratorName;
}
public String getMigratorType() {
return migratorType;
}
public void setMigratorType(String migratorType) {
this.migratorType = migratorType;
}
#Override
public String toString() {
return "Technology [migratorId=" + migratorId + ", srcTechName=" + srcTechName + ", destTechName="
+ destTechName + ", migratorName=" + migratorName + ", migratorType=" + migratorType + "]";
}
}
The join (transaction) table's entity :
#Entity
#Table(name = "project_migration")
public class ProjectMigration {
#EmbeddedId
private ProjectMigrationID migrationId;
#Column(name ="migration_finish_time")
private Calendar migrationFinishTime;
#Column(name ="time_in_millis_for_migration")
private long timeInMillisForMigration;
#Column(name ="migration_status")
private String migrationStatus;
#Column(name ="migrated_codebase_path")
private String migratedCodeBasePath;
The embedded Primary Key class is as follows:
#Embeddable
public class ProjectMigrationID implements Serializable {
private static final long serialVersionUID = -3623993529011381924L;
#Column(name = "projectid")
private String projectId;
#Column(name = "migratorid")
private String migratorId;
#Column(name = "migration_start_time")
private Calendar migrationStartTime;
public ProjectMigrationID() {
}
public ProjectMigrationID(String projectId, String migratorId, Calendar migrationStartTime) {
this.projectId = projectId;
this.migratorId = migratorId;
this.migrationStartTime = migrationStartTime;
}
The snippet from service Class :
for (String migratorId : data.getMigratorIds()) {
Migrator migrator = migratorRepository.findByMigratorId(migratorId);
migrators.add(migrator);
}
if (projectId != null) {
project = projectRepository.findByProjectID(projectId);
System.out.println(project==null);
project.setMigrators(migrators);
System.out.println("I am here");
if (project != null) {
//project.setMigrationStatus("In Progress");
ProjectMigrationID pmId = new ProjectMigrationID();
pmId.setProjectId(project.getProjectID());
pmId.setMigratorId(project.getMigrators().get(0).getMigratorId());
pmId.setMigrationStartTime(new GregorianCalendar());
ProjectMigration pm = new ProjectMigration();
pm.setMigrationId(pmId);
pm.setMigrationStatus("Pending");
projectMigrationRepository.save(pm);
That's because of the #JoinTable where the date is not included and it skips the insertion. If you include a column with all the primary keys needed, it will work as expected.
Only the columns mapped via #JoinTable will be included during insertion or update (defaults to true when mapped)
Either include the date time column in the Project class or use association without #JoinTable.
I'm editing via mobile. So please ignore typos if any.

Spring JPA EntityGraph fetches all lazy loaded properties

I've worked with Spring and Hibernate. Now having a look at Spring Data JPA (2.0.3) with JPA 2.2
AgencyTicketType
#Entity
#Table(name = "agency_ticket_type", catalog = "test")
public class AgencyTicketType implements java.io.Serializable {
private Long id;
private String name;
private Agency agency;
private Set<AgencyTicketCategory> agencyTicketCategories = new HashSet<AgencyTicketCategory>(0);
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "name", nullable = false, length = 100)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "agency_id", nullable = false)
public Agency getAgency() {
return this.agency;
}
public void setAgency(Agency agency) {
this.agency = agency;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "agencyTicketType")
public Set<AgencyTicketCategory> getAgencyTicketCategories() {
return this.agencyTicketCategories;
}
public void setAgencyTicketCategories(Set<AgencyTicketCategory> agencyTicketCategories) {
this.agencyTicketCategories = agencyTicketCategories;
}
}
AgencyTicketCategory
#Entity
#Table(name = "agency_ticket_category", catalog = "waytest")
public class AgencyTicketCategory implements java.io.Serializable {
private Long id;
private AgencyTicketType agencyTicketType;
private String name;
private BigDecimal price;
private Set<TripTicket> tripTickets = new HashSet<TripTicket>(0);
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "agency_ticket_type_id", nullable = false)
public AgencyTicketType getAgencyTicketType() {
return this.agencyTicketType;
}
public void setAgencyTicketType(AgencyTicketType agencyTicketType) {
this.agencyTicketType = agencyTicketType;
}
#Column(name = "name", nullable = false, length = 100)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "price", nullable = false, precision = 8)
public BigDecimal getPrice() {
return this.price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "agencyTicketCategory")
public Set<TripTicket> getTripTickets() {
return this.tripTickets;
}
public void setTripTickets(Set<TripTicket> tripTickets) {
this.tripTickets = tripTickets;
}
}
Repository
public interface TicketTypeRepository extends JpaRepository<AgencyTicketType, Long> {
#EntityGraph(attributePaths={ "agencyTicketCategories" }, type=EntityGraphType.LOAD)
#Query("select type from AgencyTicketType type where type.agency.code=?1")
List<AgencyTicketType> findByAgency(String agencyCode);
}
Service
#Service
public class TicketServiceImpl implements TicketService {
#Autowired private TicketTypeRepository ticketType;
#Transactional(readOnly=true)
#Override
public List<AgencyTicketType> findByName(String code) {
return ticketType.findByAgency(code);
}
}
When debugged on Service, it seems, the query eagerly fetches all the lazy loaded properties - agency, agencyTicketCategories - and all their inner lazy loaded properties, which leads to JSON serilization error.
Need to fetch only these
AgencyTicketTypes [
{
id, name,
agencyTicketCategories [
{id,name,price},....
]
},.....
]
Can I do this with #EntityGraph? What I am missing?
Specifying lazy loading is only a hint for the JPA provider. Depending on the provider you use (Hibernate, EclipseLink etc.) it may be completely ignored and the dependencies may be eagerly fetched.
What you need to do is configure how your classes are mapped to json. Assuming you are using Jackson you may need to use annotations like #JsonIgnore or #JsonView. You may also map your class that only has the fields you need.
You can use Jackson annotations #JsonBackReference/#JsonManagedReference. They address problem of infinite recursion with bidirectional links in object model. As far as I understand it is your case.
See http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion for more information.
One thing to point is that debugging while the transaction is open (touching the collection) will cause it to be loaded even if at real time it doesn't .. the other thing is that as #Apokralipsa mentioned , LAZY loading is just a hint that can be totally ignored and should never be relied upon whatever technique you are using

Spring Data Rest with Jpa relations

Followed this question but did not work
Have two entities Account and UserTransaction
Account.java
#Entity
#Access(AccessType.FIELD)
public class Account {
#Id
private Integer accountNumber;
private String holderName;
private String mobileNumber;
private Double balanceInformation;
public Account(Integer accountNumber, String holderName, String mobileNumber, Double balanceInformation) {
this.accountNumber = accountNumber;
this.holderName = holderName;
this.mobileNumber = mobileNumber;
this.balanceInformation = balanceInformation;
}
}
UserTransaction.java
#Entity
#Access(AccessType.FIELD)
#Table(name = "user_transaction")
public class Transaction {
#Id
private Long transactionId;
#ManyToOne
#JoinColumn(name = "accountNumber")
private Account accountNumber;
private Double transactionAmount;
#Column(nullable = false, columnDefinition = "TINYINT", length = 1)
private Boolean transactionStatus;
private String statusMessage;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="timestamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
private Date timestamp;
public Transaction(Long transactionId, Account account,
Double transactionAmount,
Boolean transactionStatus,
String statusMessage) {
this.transactionId = transactionId;
this.accountNumber = account;
this.transactionAmount = transactionAmount;
this.transactionStatus = transactionStatus;
this.statusMessage = statusMessage;
}
}
and My TransactionRepository is as follows
#RepositoryRestResource(collectionResourceRel = "transactions", path = "transactions")
public interface JpaTransactionRepository extends JpaRepository<Transaction, Long>, TransactionRepository {
#Query(value = "select t from Transaction t where t.accountNumber.accountNumber = :accountNumber")
Iterable<Transaction> findByAccountNumber(#Param("accountNumber") Integer accountNumber);
}
I have constructed a json as specified in the stackoverflow post at the top
{
"transactionId" : "3213435454342",
"transactionAmount" : 5.99,
"transactionStatus" : true,
"statusMessage" : null,
"timestamp" : "2017-03-09T05:11:41.000+0000",
"accountNumber" : "http://localhost:8080/accounts/90188977"
}
when I try to execute POST with the above json I get
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'account_number' cannot be null
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:533)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:513)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:115)
at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:1983)
How do I save an entity that has relationships with Spring data rest????
The problem is that with #JoinColumn(name = "accountNumber") you would hard-code the column name in database as accountNumber. Normally the naming-strategy would add embedded underscores instead of having mixed case column names.
So it should work if you change the line to #JoinColumn(name = "account_number").

How To Create Method Extend from CrudRepository for Composite Key

I have 1 class ContainerLoadList with Composite Key ContainerLoadListPK like this
#Embeddable
public class ContainerLoadListPK implements Serializable {
/**
*
*/
private static final long serialVersionUID = 3526479259987259367L;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 13)
#Column(name = "ContainerNo")
private String containerNo;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 6)
#Column(name = "VesselCode")
private String vesselCode;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 8)
#Column(name = "Voyage")
private String voyage;
public ContainerLoadListPK() {
}
public ContainerLoadListPK(String containerNo, String vesselCode, String voyage) {
this.containerNo = containerNo;
this.vesselCode = vesselCode;
this.voyage = voyage;
}
public String getContainerNo() {
return containerNo;
}
public void setContainerNo(String containerNo) {
this.containerNo = containerNo;
}
public String getVesselCode() {
return vesselCode;
}
public void setVesselCode(String vesselCode) {
this.vesselCode = vesselCode;
}
public String getVoyage() {
return voyage;
}
public void setVoyage(String voyage) {
this.voyage = voyage;
}
}
and I have another interface extended from CrudRepository like this
public interface ContainerLoadListRepository extends CrudRepository<ContainerLoadList, ContainerLoadListPK> {
List<ContainerLoadList> findByBNo(#Param("bNo") String bNo);
#Query(name = "ContainerLoadList.findByVesselCodeAndVoyage")
List<ContainerLoadList> findByVesselCodeAndVoyage(#Param("vesselCode") String vesselCode,#Param("voyage") String voyage);
}
Are You Have Another Solution for findByVesselCodeAndVoyage without using #Query?
Assuming the embedded key field inside ContainerLoadList is called pk, you can try this:
List<ContainerLoadList> findByPkVesselCodeAndPkVoyage(String vesselCode, String voyage);
For more information, check the Spring Data JPA Property Expressions documentation.

Resources