How to bind a foreign key in Vaadin 8 - java

I cannot resolve a problem. I have a window with TextField and I want to bind a foreign key but have no ideas what am I doing wrong. I've already read the answer here: Binding foreign key in Vaadin (EclipseLink)
I decided to use Converter, but still stumble.
So, there are two entities (simplified): Client and Order. An existent or a new client can have several orders, means that Order.clientID is foreign key
Client entity:
#Entity
#DynamicInsert
#DynamicUpdate
#Table(name = "Clients")
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ClientID", updatable = false, nullable = false)
private Long clientID;
#Column(name = "Name", nullable = false, length = 20)
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "clientID")
private Set<Order> orders;
public Long getId() { return clientID; }
public void setId(Long clientID) { this.clientID = clientID; }
public String getName() { return firstName; }
public void setName(String name) { this.name = name; }
}
Order entity:
#Entity
#DynamicInsert
#DynamicUpdate
#Table(name = "Orders")
public class Order{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "OrderID", nullable = false)
private Long orderID;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "ClientID",
referencedColumnName = "ClientID",
updatable = false,
nullable = false)
private Client clientID;
#Column(name = "Description", nullable = false, length = 1000)
private String description;
public Long getOrderID() { return orderID; }
//public Long getClientID() { return clientID.getId(); }
public Client getClientID() { return clientID; }
public void setClientID(Client clientID) { this.clientID = clientID; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
and I want to bind Order.clientID to the TextField. But IDEA highlight the setter setClientID as "Cannot resolve method 'setClientID'"
public class AddOrderModalView extends Window {
private OrderService orderService = new OrderService();
private Order order = new Order();
Binder<Order> binder = new Binder<>(Order.class);
private ChangeHandler changeHandler = new ChangeHandler() {
#Override
public void onChange() {
}
};
private FormLayout formLayout = new FormLayout();
private TextField clientId = new TextField("Client ID");
private TextField description = new TextField("Description");
private Button save = new Button("Save");
private Button cancel = new Button("Cancel");
public AddOrderModalView() {
super("Add a new order");
VerticalLayout subContent = new VerticalLayout();
subContent.setSizeFull();
HorizontalLayout actions = new HorizontalLayout();
actions.addComponents(save, cancel);
formLayout.addComponents(clientId, description);
subContent.addComponent(formLayout);
setContent(subContent);
save.addStyleNames(ValoTheme.BUTTON_SMALL, ValoTheme.BUTTON_PRIMARY);
cancel.addStyleName(ValoTheme.BUTTON_SMALL);
save.addClickListener(e -> save());
cancel.addClickListener(e -> close());
bindingFields();
setModal(true);
}
private void bindingFields() {
binder.forField(clientId)
.withConverter(Long::valueOf, String::valueOf)
.bind(Order::getClientID, Order::setClientID); //the error is here
binder.forField(this.description)
.withValidator(new StringLengthValidator(
"Please add description. The maximum length is 1000 characters", 1, 1000))
.bind(Order::getDescription, Order::setDescription);
binder.bindInstanceFields(this);
binder.setBean(order);
}
public interface ChangeHandler {
void onChange();
}
private void save() {
if (binder.validate().isOk()) {
orderService.persist(order);
close();
changeHandler.onChange();
}
}
}
ClientToClientIdConverter:
public class ClientToClientIdConverter implements Converter<String, Client> {
#Override
public Result<Client> convertToModel(String s, ValueContext valueContext) {
return Result.error("not supported");
}
#Override
public String convertToPresentation(Client client, ValueContext valueContext) {
return Objects.toString(client.getId(), "");
}
}
Could anyone help me with solving the problem?

Answer to the question: How to bind foreign key (or any other nested property) in a textfield (not what you need!)
You can do it by providing lambda expressions to get and set the nested properties.
TextField clientId = new TextField("Client ID");
binder.forField(clientId)
.withConverter(new StringToLongConverter("error message"))
.bind(item -> item.getClient().getId(), (item, value) -> item.getClient().setId(value));
This code can be cause of NullPointerExceptions if the order can have no client at this point. If that is possible, then use this instead (added checking for nullvalues):
TextField clientId = new TextField("Client ID");
binder.forField(clientId)
.withConverter(new StringToLongConverter("error message"))
.bind(
item -> item.getClient() != null ? item.getClient.getId() : null,
(item, value) -> {
if(item.getClient() != null){
item.getClient().setId(value);
}
});
Warning! Please know that manually changing the value in this textfield will change the id of it's already assigned client, and not select/assign a new Client for this Order. If it's the latter that you want, use a ComboBox instead! I'm not sure if it ever makes sense to do the first, but I answered because you asked. I am now sure that you want the latter option, so please follow the next part.
The actual solution to your problem: It seems that you indeed need a ComboBox, because you want to select/assign a client for the order.
So what you need is basically this:
ComboBox<Client> clientSelection = new ComboBox<Client>("client");
clientSelection.setItems(clientService.findAll()); // list/set of possible clients.
// Using clients name for item captions now, but you can also use id or both
clientSelection.setItemCaptionGenerator(Client::getName);
binder.forField(clientSelection)
.bind(Order::getClient, Order::setClient);
This way, you can select a client which then gets set as the bound orders client.

Your binding doesnt work because your setClient method expects an Object of type Client as parameter not an Long. You should change that to:
public void setClientID(Long clientID) { this.clientID = clientID; }
You could also use an ComboBox for the Selection of Customer, there is an example how to bind Objects to a ComboBox on the Vaadin Website.

Related

Vaadin - Internal error Please notify the administrator. Take note of any unsaved data, and click here or press ESC to continue

I am developing an application in Vaadin where I have a view for Suppliers.
I am getting the error Internal error Please notify the administrator. Take note of any unsaved data, and click here or press ESC to continue. due to the calling of the method categoriesToString in my View.
View
public SupplierView(CrmService service) {
this.service = service;
addClassName("supplier-view");
setSizeFull();
configureGrid();
form = new SupplierForm(service.findAllCategories(null));
form.setWidth("25em");
form.addListener(SupplierForm.SaveEvent.class, this::saveSupplier);
form.addListener(SupplierForm.DeleteEvent.class, this::deleteSupplier);
form.addListener(SupplierForm.CloseEvent.class, e -> closeEditor());
FlexLayout content = new FlexLayout(grid, form);
content.setFlexGrow(2, grid);
content.setFlexGrow(1, form);
content.setFlexShrink(0, form);
content.addClassNames("content", "gap-m");
content.setSizeFull();
add(getToolbar(), content);
updateList();
closeEditor();
grid.asSingleSelect()
.addValueChangeListener(event -> editSupplier(event.getValue()));
}
private void configureGrid() {
grid.addClassNames("supplier-grid");
grid.setSizeFull();
grid.setColumns("name", "address");
grid.addColumn(supplier -> supplier.categoriesToString())
.setHeader("Categories");
grid.getColumns().forEach(col -> col.setAutoWidth(true));
}
}
The said method belongs to the class Supplier that can be seen below. This class has a Set of Category that I am trying to go trough in order to return all categories present in it.
Supplier
#Entity
public class Supplier extends AbstractEntity {
#NotEmpty
private String name;
#NotEmpty
private String address;
#OneToMany(mappedBy = "supplier")
private Set<Category> categories = new LinkedHashSet<>();
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
public Set<Category> getCategories() {
return categories;
}
public void setCategories(Set<Category> categories) {
this.categories = categories;
}
public String categoriesToString(){
String allCategories = "";
for (Category c : this.categories)
{
allCategories += c.getDescription();
}
return allCategories;
}
}
In order to do some testing I tried to make the method just return "test" and in that scenario the word was indeed added to "Categories" column and no error was shown.
Category class
#Entity
public class Category extends AbstractEntity{
#NotBlank
private String description;
#ManyToOne
#JoinColumn(name = "supplier_id")
#JsonIgnoreProperties({"categories"})
private Supplier supplier;
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
Why is this happening? Am I iterating through the set in a incorrect way?
You have not specified the fetch mode for your Entity, so it is defaulting to Lazy mode. Your current logic will only work with Eager mode (that would fetch the "categories" one-to-many relationship), but this is bound to create performance issues down the road as your data grows.
I wouldn't use logic inside my entities either (your categoriesToString method), but that is a different topic altogether.
You can either specify Eager loading on your entity as:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "supplier")
private Set<Category> categories = new LinkedHashSet<>();
Or perform an additional query to fetch the related categories. Or even better, create a POJO or Spring Data Projection to fetch/map the appropriate data to. You can check this link for more info on that matter.

Java, list sorting with reflection

I want to allow to sort by every field in the class, without having to write switch/ if statements.
My idea was to find the Field that matches given string value by name and then, with Stream API neatly sort. IntelliJ screamed that i need to surround it with try-catch, so it is not so neatly looking, but that's not important, as it does not work.
private List<MyEntity> getSorted(List<MyEntity> list, SearchCriteria criteria) {
Field sortByField = findFieldInListByName(getFieldList(MyEntity.class), criteria.getSortBy());
return list.stream().sorted(Comparator.comparing(entity-> {
try {
return (MyEntity) sortByField.get(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return entity;
})).collect(Collectors.toList());
}
In the MyEntity class I have added Comparable interface, but I am not sure what should be in the body of Compare(), as I dont want to specify how to compare objects, because it will change based on the selected sorting.
EDIT: Added Entity below:
#Data
#Entity
#Table(name = "role_management", schema = "mdr")
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class MyEntity implements Comparable{
#Id
#Column(name = "uuid", unique = true, insertable = false, updatable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID uuid;
#ManyToOne
#JoinColumn(name = "user_id", nullable = false)
private UserEntity user;
#Basic
#NonNull
#Column(name = "role")
private String role;
#Basic
#Column(name = "action")
#Enumerated(EnumType.STRING)
private RoleAction action;
#Basic
#Column(name = "goal")
#Enumerated(EnumType.STRING)
private RoleGoal goal;
#Column(name = "date")
private LocalDateTime date;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reporter_id", referencedColumnName = "uuid")
private UserEntity reporter;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "authorizer_id", referencedColumnName = "uuid")
private UserEntity authorizer;
#Basic
#Column(name = "ezd")
private String ezd;
#Basic
#Column(name = "is_last")
private boolean isMostRecent;
#Override
public int compareTo(Object o) {
return 0;
}
}
EDIT 2: My code based on the #Sweeper solution:
UserEntity (nullable)
#Override
public int compareTo(UserEntity other) {
if (other == null) {
return 1;
}
return this.getMail().compareTo(other.getMail());
}
Comparator:
public static Comparator getSortComparator(Field sortByField) {
return Comparator.nullsLast(Comparator.comparing(entity -> {
try {
Object fieldValue = sortByField.get(entity);
if (!(fieldValue instanceof Comparable<?>) && fieldValue != null) {
throw new IllegalArgumentException("...");
}
return (Comparable) fieldValue;
} catch (IllegalAccessException e) {
throw new MdrCommonException(e.getLocalizedMessage());
}
}));
}
MyEntity should not implement Comparable. It is the fields, by which you are going to sort the list of MyEntity objects, that needs to be Comparable. For example, if you are sorting by the field user, which is a UserEntity, then UserEntity is the thing that needs to be comparable, not MyEntity.
The lambda's job should just be to check that the fields are indeed Comparable, and throw an exception if they are not.
Since you don't know the types of the fields at compile time, however, you'd have to use a raw type here. The comparing call would look like this:
Comparator.comparing(entity -> {
try {
Object fieldValue = sortByField.get(entity);
// This check still passes if the type of fieldValue implements Comparable<U>,
// where U is an unrelated type from the type of fieldValue, but this is the
// best we can do here, since we don't know the type of field at compile time
if (!(fieldValue instanceof Comparable<?>) && fieldValue != null) {
throw new IllegalArgumentException("Field is not comparable!");
}
return (Comparable)fieldValue;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
})
You can create automatically comparators for any field of any class using reflection but is better create specific comparators (will be typechecked).
Your entity is a normal class with normal fields then, the usual Java sorting machinery should do the job:
Basically, if you define one comparator for every field (even deep fields into your entity):
public final static Comparator<MyEntity> ByField1 = comparing(MyEntity::getField1);
public final static Comparator<MyEntity> ByField2 = comparing(MyEntity::getField2);
public final static Comparator<MyEntity> ByField3 = comparing(MyEntity::getField3);
public final static Comparator<MyEntity> ByDeep1 = comparing(a -> a.getField4().getDeep1());
public final static Comparator<MyEntity> ByDeep2 = comparing(a -> a.getField4().getDeep2());
public final static Comparator<MyEntity> ByDeep3 = comparing(a -> a.getField4().getDeep3());
You can sort using complex sorting expressions:
data.stream()
.sorted(ByField2.reversed().thenComparing(ByDeep2))
.forEach(System.out::println);
a full example could be
public static void main(String[] args) {
List<MyEntity> data =
Stream.of("Row1", "Row2").flatMap(field1 ->
Stream.of(101, 102).flatMap(field2 ->
Stream.of(true, false).flatMap(field3 ->
Stream.of("Row1", "Row2").flatMap(deep1 ->
Stream.of(101, 102).flatMap(deep2 ->
Stream.of(true, false).map(deep3 ->
new MyEntity(field1, field2, field3, new MyDeepField(deep1, deep2, deep3))))))))
.collect(toList());
data.stream()
.sorted(ByField2.reversed().thenComparing(ByDeep2))
.forEach(System.out::println);
}
#Getter
#Setter
#AllArgsConstructor
static class MyDeepField {
private String deep1;
private Integer deep2;
private Boolean deep3;
}
#Getter
#Setter
#AllArgsConstructor
static class MyEntity {
private String field1;
private Integer field2;
private Boolean field3;
private MyDeepField field4;
public final static Comparator<MyEntity> ByField1 = comparing(MyEntity::getField1);
public final static Comparator<MyEntity> ByField2 = comparing(MyEntity::getField2);
public final static Comparator<MyEntity> ByField3 = comparing(MyEntity::getField3);
public final static Comparator<MyEntity> ByDeep1 = comparing(a -> a.getField4().getDeep1());
public final static Comparator<MyEntity> ByDeep2 = comparing(a -> a.getField4().getDeep2());
public final static Comparator<MyEntity> ByDeep3 = comparing(a -> a.getField4().getDeep3());
#Override
public String toString() {
return "MyEntity{" +
"field1='" + field1 + '\'' +
", field2=" + field2 +
", field3=" + field3 +
", deep1=" + field4.getDeep1() +
", deep2=" + field4.getDeep2() +
", deep3=" + field4.getDeep3() +
'}';
}
}
with output
MyEntity{field1='Row1', field2=102, field3=true, deep1=Row1, deep2=101, deep3=true}
MyEntity{field1='Row1', field2=102, field3=true, deep1=Row1, deep2=101, deep3=false}
...
MyEntity{field1='Row2', field2=101, field3=false, deep1=Row2, deep2=102, deep3=true}
MyEntity{field1='Row2', field2=101, field3=false, deep1=Row2, deep2=102, deep3=false}
The criteria field into your SearchCriteria class is some field of type Comparator<MyEntity> or a mapping using an enumeration or parsing string expressions or so...

How to send only the ID the of main nested objects in the body request in spring boot

I'm creating eCommerce for merchants using spring boot with JPA.
I have an issue while creating the order service.
I want to only pass the ID of the nested objects in the request body instead of sending the full nest objects because the size will be extremely big.
Here is my code.
Merchant can do many orders
Order
#Entity
#Table(name = "Orders")
#XmlRootElement
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Order extends BasicModelWithIDInt {
#Basic(optional = false)
#Column(name = "Quantity")
private Integer quantity;
#Basic(optional = false)
#Size(min = 1, max = 150)
#Column(name = "Notes")
private String notes;
#JoinColumn(name = "ProductID", referencedColumnName = "ID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JsonIgnoreProperties
private Product productID;
#JoinColumn(name = "MerchantID", referencedColumnName = "ID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Merchent merchent;
#JoinColumn(name = "OrderSatusID", referencedColumnName = "ID")
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private OrderStatus orderStatus;
// Getters and Setters
}
Order Holder
public class OrderHolder {
#NotNull
private Order order;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
OrderRepo
public interface OrderRepo extends JpaRepository<Order, Integer> {
}
Order Controller
#RestController
#RequestMapping(value = "order", produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderRestController extends BasicController<OrderHolder>{
#Autowired
private OrderRepo orderRepo;
#PostMapping("create")
public ResponseEntity<?> create(#RequestBody #Valid OrderHolder orderHolder, Principal principal) throws GeneralException {
log.debug( "create order {} requested", orderHolder.toString());
Order order = new Order();
order = orderHolder.getOrder();
System.out.println("###############"+order);
try {
order = orderRepo.save(order);
log.info( "Order {} has been created", order );
} catch (Exception e) {
log.error( "Error creating Order: ", e );
e.printStackTrace();
throw new GeneralException( Errors.ORDER_CREATION_FAILURE, e.toString() );
}
return ResponseEntity.ok( order );
}
}
I need request body to look like the below instead of including the full Merchant and Product objects inside the request.
You can make use of JsonView to return only id of product and merchant
public class OrderView {}
...
public class Product{
#Id
#JsonView(OrderView.class)
private Integer id
private String otherFieldWithoutJsonView
...
}
and then in your controller
#PostMapping("create")
#JsonView(OrderView.class) // this will return the product object with one field (id)
public ResponseEntity<?> create(#RequestBody #Valid OrderHolder orderHolder, Principal principal) throws GeneralException {
...
}
hope this can help you
Just have a separate contract class.
public class OrderContract {
private int merchantID;
private String notes;
....
//getter, setters
}
public class OrderHolder {
#NotNull
private OrderContract orderContract;
public OrderContract getOrderContract() {
return orderContract;
}
public void setOrder(OrderContract orderContract) {
this.orderContract = orderContract;
}
}
And before making a call to the Repository , translate from OrderContract to Order.
I would like to share something regarding this.
I have searched a lot on internet and tried lot of things, but the solution given here suited well for this scenario.
https://www.baeldung.com/jackson-deserialization
You need to create a Custom-deserializer for your model by extending StdDeserializer from com.fasterxml.jackson.databind.deser.std.StdDeserializer, where you just want to pass id's and not the whole object in the request.
I have given below example for User Model with Address object.
User(long userId, String name, Address addressId)
Address(long addressId, String wholeAddress)
Writing Deserializer for User class
public class UserDeserializer extends StdDeserializer<User> {
public User() {
this(null);
}
public User Deserializer(Class<?> vc) {
super(vc);
}
#Override
public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
JsonNode node = p.getCodec().readTree(p);
long id = 0;
long addressId = (Long) ((IntNode) node.get("addressId")).numberValue().longValue();
return new User(id, name, new Address(addressId, null)
}
Now you have to use
#JsonDeserialize(using = UserDeserializer.class)
public Class User {
...
}
POST request
Before custom deserialization
{
"name" : "Ravi",
"addressId" : { "id" : 1}
}
After custom Deserialization
{
"name" : "Ravi",
"addressId" : 1
}
Also while GET /user/:id call you will get the whole obj like
{
"name" : "Ravi",
"addressId" : { "id" : 1, "wholeAddress" : "Some address"}
}

Hibernate call DELETE from table after method end

I have problem, and I don't know how to solve it.
I have entity:
#Entity
#Table(name = "entity_languagetree")
#AttributeOverride(name = "id", column = #Column(name = "languagetree_id"))
public class LanguageTree extends BaseObject {
#ElementCollection(targetClass = java.lang.String.class, fetch = FetchType.EAGER)
#CollectionTable(name = "view_languagetree_to_stringlist")
private List<String> relationship = new ArrayList<>();
public LanguageTree() {
//
}
public List<String> getRelationship() {
return relationship;
}
public void setRelationship(List<String> relationship) {
this.relationship = relationship;
}
}
where BaseObject is
#MappedSuperclass
public class BaseObject {
#Id
#GeneratedValue
#Column(name = "entity_id")
private Long id;
/**
*
* #return true if the entity hasn't been persisted yet
*/
#Transient
public boolean isNew() {
return id == null;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Bean getBean() {
return null;
}
}
Work with object - in my servlet, I am calling jsVarTree() like this:
String var = jsVarTree();
My problem is, that after method jsVarTree is finished, hibernate delete my relationship list from entity LanguageTree. I don't know why! I am not calling any delete and etc.. (I AM SURE, I SPENT A LOT OF TIME IN DEBUGER!)
:
#Override
public String jsVarTree() {
TreeBuilder tb = new TreeBuilder(getLanguageList());
return tb.getJsVarString(); // THIS METHOD IS ONLY GETTER !!!!
}
#Override
public List<String> getLanguageList() {
LanguageTree lt = getLanguageTreeObject();
return lt.getRelationship();
}
#Override
public LanguageTree getLanguageTreeObject() {
long fakingId = languageTreeDao.getLastId();
ServerLogger.logDebug("LAST FAKING ID: " +fakingId);
return languageTreeDao.findOne(fakingId);
}
I found this log in loggor:
HibernateLog --> 15:01:03 DEBUG org.hibernate.SQL - delete from
view_languagetree_to_stringlist where LanguageTree_languagetree_id=?
Can somebody tell me, why hibernate call delete over my table?
I saw a table in phpmyadmin..
TABLE IS FULL.
String var = jsVarTree();
TABLE IS EMPTY.
Table is deleted after return tb.getJsVarString(); is finished.
Thank you for any help!

Does Jpa #Repository automatically update the object when a setter is called?

While programming in test driven development I stumbled upon a strange thing. My test does not fail, even when I don't update the object to the database.
#Test
public void testStartCircleSession(){
Circle circle=circleSessionService.createCircle(defaultTheme,2,2,GameMode.ONLINE);
circle.setGameStatus(GameStatus.STARTED);
//circleSessionService.updateCircle(defaultCircle); --> this calls the update method
Circle circleFromRepo=circleRepository.findOne(circle.getId());
assertThat(circleFromRepo.getGameStatus(),equalTo(circle.getGameStatus()));
}
By default the gamemode gets set to PLANNED yet the test finished successfully without having called the update method. So I strongly believe Jpa updated the object when a setter is called, but I'm not sure.
Circle DOM
package be.kdg.kandoe.backend.dom;
import be.kdg.kandoe.backend.dom.participations.CircleParticipation;
import be.kdg.kandoe.backend.dom.roles.Role;
import javafx.beans.DefaultProperty;
import org.springframework.hateoas.Identifiable;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "Circle")
public class Circle implements Serializable, Identifiable<Integer>{
#Id
#GeneratedValue
#Column(name = "CircleId", nullable = false)
private Integer circleId;
#OneToMany(targetEntity = CircleParticipation.class,cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy = "circle")
private List<CircleParticipation> circleParticipations;
#OneToMany(targetEntity = Card.class,cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy = "circle")
private List<Card> cards;
#OneToMany(targetEntity = Vote.class,cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy = "circle")
private List<Vote> votes;
#OneToOne(targetEntity = Theme.class, cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name="ThemeId",nullable = false)
private Theme theme;
#Column(name = "GameMode", nullable = false)
#Enumerated(EnumType.STRING)
private GameMode gameMode;
#Column(name = "GameStatus", nullable = false)//,columnDefinition ="PLANNED")
#Enumerated(EnumType.STRING)
private GameStatus gameStatus;
#Column(name = "TurnTime", nullable = false)
private Integer turnTime;
#Column(name = "TotalRounds", nullable = false)
private Integer totalRounds;
#OneToOne(targetEntity = CircleParticipation.class, cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name="CurrentCircleParticipationId") //current user
private CircleParticipation currentCircleParticipation;
#Column(name = "CurrentRound", nullable = false)
private Integer currentRound;
public CircleParticipation getCurrentCircleParticipation() {
return currentCircleParticipation;
}
public void setCurrentCircleParticipation(CircleParticipation currentCircleParticipation) {
this.currentCircleParticipation = currentCircleParticipation;
}
public GameMode getGameMode() {
return gameMode;
}
public Integer getTurnTime() {
return turnTime;
}
public Integer getTotalRounds() {
return totalRounds;
}
public Circle(Theme theme, int turnTime, int totalRounds, GameMode mode){
this.theme = theme;
this.turnTime = turnTime;
this.totalRounds = totalRounds;
this.gameMode = mode;
this.currentRound=1;
circleParticipations = new ArrayList<>();
gameStatus=GameStatus.PLANNED;
}
public Circle() {
circleParticipations = new ArrayList<>();
}
public Integer getCircleId() {
return circleId;
}
public List<Vote> getVotes() {
return votes;
}
public List<Card> getCards() {
return cards;
}
public Theme getTheme() {
return theme;
}
#Override
public Integer getId() {
return circleId;
}
public List<CircleParticipation> getCircleParticipations() {
return circleParticipations;
}
public Integer getCurrentRound() {
return currentRound;
}
public void setCurrentRound(int currentRound) {
this.currentRound = currentRound;
}
public CircleParticipation getCreatorParticipation() {
return this.circleParticipations.stream().filter(p->p.getRoles().contains(Role.toRole(Role.RoleType.CREATOR))).findFirst().get();
}
public GameStatus getGameStatus() {
return gameStatus;
}
public void setGameStatus(GameStatus gameStatus) {
this.gameStatus = gameStatus;
}
}
Repo
package be.kdg.kandoe.backend.persistence.api;
import be.kdg.kandoe.backend.dom.Circle;
import be.kdg.kandoe.backend.dom.Theme;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* Created by claudiu on 23/02/16.
*/
public interface CircleRepository extends JpaRepository<Circle,Integer>, JpaSpecificationExecutor<Circle> {
}
I would have to say yes. I did a test, but slightly different than what you have done.
I first created a Car and set the type to honda, similar to what you did:
Car car = new Car();
carRepository.save(car);
car.setType("honda");
System.out.println("CAR="+car);
Notice that the save is done before the type is set. Not surprisingly, the Car prints out as type "honda":
CAR=Car:1:honda
When I do a separate fetch, in an entirely different ServiceImpl call, the type is still "honda"
Car c = carRepository.findOne(id);
System.out.println("CAR="+c);
Which seems to indicate that the car type was saved at least in a cache somewhere. However, I understand that object caches, which I think are level II caches, are not enabled by default, which I haven't done.
Still, to check further, I added a change car method:
Car c = carRepository.findOne(id);
c.setType("toyota");
System.out.println("CAR="+c);
Here, of course, the car prints out as type = "Toyota", as expected:
CAR=Car:1:toyota
But what indicates that setting the field has resulted in an update in the database is two things. The first, and most conclusive, is there is an database update statement from hibernate after I exit the method:
Hibernate: update car set type=? where id=?
And, secondly, there the type returned from a subsequent and separate find:
Car c = carRepository.findOne(id);
System.out.println("CAR="+c);
shows the type as a "Toyota":
CAR=Car:1:toyota
you are trying to repeat the process after the end of the transaction
that you have achieved
try this
#Test
public void testStartCircleSession(){
Circle circle=Circle(defaultTheme,2,2,GameMode.ONLINE).setGameStatus(GameStatus.STARTED);
circleSessionService.createCircle(circle);
Circle circleFromRepo=circleRepository.findOne(circle.getId());
assertThat(circleFromRepo.getGameStatus(),equalTo(circle.getGameStatus()));
}

Categories