I'm getting an error saying that my entity is detached when I try to persist it. When debugging I can see that the objects I try to save have an ID. I am guessing that this might be related to my annotations in JPA but I cannot figure out what is the cause of this problem.
Class with method that causes error(the last line of doStuff() is responsible):
#RestController
#RequestMapping("/test")
public class ConcreteMyController implements MyController {
private final CategoryService categoryService;
private final OrmFactory ormFactory;
private final SongService songService;
private final CategoryForSongService categoryForSongService;
#Autowired
public ConcreteMyController(CategoryService categoryService, OrmFactory ormFactory, SongService songService, CategoryForSongService categoryForSongService) {
this.categoryService = categoryService;
this.ormFactory = ormFactory;
this.songService = songService;
this.categoryForSongService = categoryForSongService;
}
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.OK)
#Override
public void doStuff() {
String title = "BestSongInTheWorld";
String popCategoryName = "pop";
String rockCategoryName = "rock";
String jazzCategoryName = "jazz";
Song song = this.ormFactory.createSong(title, "3:14");
this.songService.save(song);
Set<Category> categories = Sets.newHashSet(new Category(popCategoryName), new Category(rockCategoryName), new Category(jazzCategoryName));
this.categoryService.save(categories);
Song retrievedSong = songService.get(title);
Set<Category> retrievedCategories = categoryService.get(Sets.newHashSet(popCategoryName, rockCategoryName));
CategoryForSong categoryForSong = new CategoryForSong(retrievedSong.getTitle(), retrievedCategories);
this.categoryForSongService.save(categoryForSong);
}
}
Entities:
#Entity
#Table
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "name")
private String name;
#Id
#GeneratedValue
private Long id;
}
#Entity
#Table
public class Song implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "duration")
private String duration;
#Column(name = "title")
private String title;
#Id
#GeneratedValue
private Long id;
}
#Entity
#Table
public class CategoryForSong implements Serializable {
private static final long serialVersionUID = 1L;
#JoinColumn(name = "categories")
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Category> categories;
#Column(name = "songTitle")
private String songTitle;
#Id
#GeneratedValue
private Long id;
}
Getters, setters, equals etc. has been omitted. The reason that CategoryForSong contains a string with a songTitle instead of an actual Song is just me trying to isolate the problem. That did not seem to help.
My services are not very interesting, so I will add the one for Song here just to show the structure:
#Service
public class ConcreteSongService implements SongService {
#Autowired
private SongRepository songRepository;
#Transactional
#Override
public void save(Song song) {
songRepository.save(song);
}
#Transactional
#Override
public Song get(String title) {
return songRepository.getByTitle(Sets.newHashSet(title)).stream().findFirst().get();
}
}
And the repository is as simple as this:
public interface SongRepository extends CrudRepository<Song, Long> {
Set<Song> getByTitle(Set<String> titles);
}
The exception looks like this:
org.hibernate.PersistentObjectException: detached entity passed to persist: proofofconcept.springmvc.model.orm.Category
Help would be very appreciated.
Related
I tried to select all columns from the table MAGICNOTIFY_CARD_INFO, so i wrote a code;
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MagicnotifyApplication.class, args);
MagicnotifyCardInfoRepository magicnotifyCardInfoRepository =
context.getBean(MagicnotifyCardInfoRepository.class);
magicnotifyCardInfoRepository.findAll();
//SpringApplication.run(MagicnotifyApplication.class, args);
}
and this is the entity i wanted to select;
public class MagicnotifyCardInfoID implements Serializable {
#Column(name = "koname")
private String koname;
#Column(name = "name")
private String name;
#Column(name = "cardkingdom")
private String cardkingdom;
#Column(name = "cardkingdomfoil")
private String cardkingdomfoil;
#Column(name = "set")
private String set;
#Column(name = "setName")
private String setName;
#Column(name = "reldate")
private Date reldate;
#Column(name = "rarity")
private String rarity;
#Column(name = "uuid")
private String uuid;
#ManyToOne
private MagicnotifyUuidName magicnotifyUuidName;
#ManyToOne
private MagicnotifySetInfo magicnotifySetInfo;
}
public class MagicnotifyCardInfo implements Serializable {
#EmbeddedId
private MagicnotifyPriceID id;
}
public interface MagicnotifyCardInfoRepository extends JpaRepository<MagicnotifyCardInfo, Long> {
#Query(value = "SELECT * FROM MAGICNOTIFY_CARD_INFO", nativeQuery = true)
List<MagicnotifyCardInfo> findByAll();
List<MagicnotifyCardInfo> findAll();
}
but after querying, it tries to select other column item from table
MAGICNOTIFY_PRICE;
public class MagicnotifyPriceID implements Serializable {
#Column(name = "foil")
private BigDecimal foil;
#Column(name = "normal")
private BigDecimal normal;
#Column(name = "date")
private Date date;
#Column(name = "key")
private String key;
#ManyToOne
private MagicnotifyUuidName id;
}
public class MagicnotifyPrice implements Serializable {
#EmbeddedId
private MagicnotifyPriceID id;
}
I'm not sure why it happens from differently mapped two tables; how can i select from initial table MAGICNOTIFY_CARD_INFO and select from its columns?
First of all, you have not mentioned any primary key using #Id annotation inside either of your MagicnotifyCardInfoID class or MagicnotifyPriceID class
Secondly, you have given same #EmbeddedId fields "MagicnotifyPriceID id" in both the below classes
public class MagicnotifyCardInfo implements Serializable {
#EmbeddedId
private MagicnotifyPriceID id;
}
public class MagicnotifyPrice implements Serializable {
#EmbeddedId
private MagicnotifyPriceID id;
}
I don't see #Embeddable used anywhere in your program
Please refer https://www.baeldung.com/jpa-embedded-embeddable
public interface MagicnotifyCardInfoRepository extends JpaRepository<MagicnotifyCardInfo, Long> {
#Query(value = "SELECT * FROM MAGICNOTIFY_CARD_INFO", nativeQuery = true)
List<MagicnotifyCardInfo> findByAll();
List<MagicnotifyCardInfo> findAll();
}
In the above class you are passing "JpaRepository<MagicnotifyCardInfo, Long>"
Long as the data type of a primary key in your entity "MagicnotifyCardInfo"
which does not even exist.
Please fix these and try again.
I want to have a simple parent children relationship but somehow it does not work and I don't get what is missing.
Parent Mapper Interface (adding uses = {LayerMapper.class} does not change anything):
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
#DecoratedWith(MlpConfigMapperDecorator.class)
public interface MlpConfigMapper {
#Mapping(target = "epochNumber", source = "epochs")
#Mapping(target = "activationFunction", ignore = true)
MlpConfig toEntity(CustomMlpConfigRequest mlpConfigDto);
}
Parent decorator according to this answer (https://stackoverflow.com/a/60217018/10565504):
public abstract class MlpConfigMapperDecorator implements MlpConfigMapper {
#Autowired
#Qualifier("delegate")
private MlpConfigMapper delegate;
#Autowired
private ActivationFunctionService activationFunctionService;
#Override
public MlpConfig toEntity(CustomMlpConfigRequest mlpConfigDto) {
MlpConfig mlpConfig = delegate.toEntity(mlpConfigDto);
mlpConfig.setActivationFunction(activationFunctionService.findByType(mlpConfigDto.getActivationFunction()));
return mlpConfig;
}
}
The Parent DTO:
public class CustomMlpConfigRequest {
private String name;
private String description;
private int batchSize;
private int epochs;
private List<LayerDto> layers;
private String activationFunction;
}
The Child DTO:
public class LayerDto {
public String type;
public int orderNumber;
public int neuronsNumber;
}
Parent Entity:
public class MlpConfig {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private String description;
private int batchSize;
private int epochNumber;
#JoinColumn(nullable = false, name = "activationFunction_id")
#ManyToOne(fetch = FetchType.LAZY)
private ActivationFunction activationFunction;
#JsonManagedReference
#Column(nullable = false)
#OneToMany(mappedBy = "mlpConfig", cascade = CascadeType.ALL)
private List<Layer> layers;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
private Date lastUpdated;
}
Child Entity:
public class Layer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private ELayer type;
private int neuronsNumber;
private int orderNumber;
#EqualsAndHashCode.Exclude
#ToString.Exclude
#ManyToOne(fetch = FetchType.LAZY, optional=false)
#JoinColumn(name = "mlpConfig_id", nullable=false)
#JsonBackReference
private MlpConfig mlpConfig;
}
Generated Child Entity Mapper Method (setChildren or setMlpConfig() in my case is missing):
#Override
public LayerDto layerToDto(Layer layer) {
if ( layer == null ) {
return null;
}
LayerDto layerDto = new LayerDto();
if ( layer.getType() != null ) {
layerDto.setType( layer.getType().name() );
}
layerDto.setOrderNumber( layer.getOrderNumber() );
layerDto.setNeuronsNumber( layer.getNeuronsNumber() );
return layerDto;
}
How do I get the mapper to set the parent in the child?
do you have Layer toEntity(LayerDto layerDto);
Also, on setter of layers on entity class, you should say, layers.forEach (layer -> layer.setmlpConfig(this));
Did you do that setting job? If you don't, layers of mplConfig entity can always be null when you try to get it.
In the end I fixed it myself. I don't know if it is best practice and probably the mapper should do it without manual help but at least it works:
public abstract class MlpConfigMapperDecorator implements MlpConfigMapper {
#Autowired
#Qualifier("delegate")
private MlpConfigMapper delegate;
#Autowired
private ActivationFunctionService activationFunctionService;
#Override
public MlpConfig mlpConfigToEntity(CustomMlpConfigRequest mlpConfigDto) {
MlpConfig mlpConfig = delegate.mlpConfigToEntity(mlpConfigDto);
mlpConfig.setActivationFunction(activationFunctionService.findByType(mlpConfigDto.getActivationFunction()));
//this is the difference. I set the config for the layer manually
mlpConfig.getLayers().forEach(e -> e.setMlpConfig(mlpConfig));
return mlpConfig;
}
}
I have the two entities with a manyToMany relationship:
#Entity
#Table(name="categories")
public class CategoryEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private int categoryId;
#Column(name="name")
private String CategoryName;
#ManyToMany(mappedBy = "categories")
private List<ProductEntity> products = new ArrayList<ProductEntity>();
}
#Entity
#Table(name="products")
public class ProductEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Integer productId;
#Column(name="name")
private String productName;
#Column(name="description")
private String description;
#Column(name="price")
private Float price;
#Column(name="rating")
private Float rating;
#Column(name="image")
private String image;
#Column(name="quantity")
private Integer quantity;
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "product_category",
joinColumns = {#JoinColumn(name = "product_id")},
inverseJoinColumns = {#JoinColumn(name = "category_id")}
)
private List<CategoryEntity> categories = new ArrayList<>();
}
In the database I have a join Table product_category that hold the product_id and category_id.
my question is how to add element to the joinTable product_category? is it possible to create a Repository even if we don't have an entities??
I tried this with my controller:
public class ProductController {
#Autowired
private ProductService productService;
#Autowired
private ProductMapper productMapper;
#Autowired
private CategoryMapper categoryMapper;
#Autowired
private CategoryService categoryService;
#Autowired
private ProductReviewService reviewService;
#Autowired
private ProductReviewMapper reviewMapper;
#PostMapping("/products")
public ResponseEntity<ProductDto> createProduct(#RequestBody ProductDto productDto) {
ProductEntity productEntity=productMapper.dtoToEntity(productDto);
for(CategoryDto categoryDto:productDto.getCategories()){
CategoryEntity categoryEntity=categoryMapper.dtoToEntity(categoryDto);
productEntity.getCategories().add(categoryEntity);
}
productEntity=productService.saveProduct(productEntity);
productDto.setProductId(productEntity.getProductId());
return ResponseEntity.created(null).body(productDto);
}
}
but I got this:
org.hibernate.PersistentObjectException: detached entity passed to persist: com.be.ec.entities.CategoryEntity
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
at
You have relationship consistency issue. you are adding a category to a product but not adding product into category
add this method into your ProductEntity class:
public void addCategory(CategoryEntity category) {
this.getCategories().add(category);
category.getProducts().add(this);
}
and use this method to add category into product.
ProductEntity productEntity=productMapper.dtoToEntity(productDto);
for(CategoryDto categoryDto:productDto.getCategories()){
CategoryEntity categoryEntity=categoryMapper.dtoToEntity(categoryDto);
productEntity.addCategory(categoryEntity); //changed line
}
productEntity=productService.saveProduct(productEntity);
productDto.setProductId(productEntity.getProductId());
I have a class UserAccountEntity that is in a one-to-one relation with UserEntity:
#Entity
#Table(name = "user_entity")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 2059380022311094257L;
#Id
#GeneratedValue(generator = RandomAlphanumericIdGenerator.generatorName)
#GenericGenerator(name = RandomAlphanumericIdGenerator.generatorName, strategy = "com.myproject.project.core.utils.RandomAlphanumericIdGenerator")
private String id;
public UserEntity() {}
}
#Entity
#Table(name="user_account_entity")
public class UserAccountEntity implements UserDetails {
#Id
private String id;
#MapsId
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private UserEntity user;
}
Now I want to add another class UserAccountActivityCode, which should be in one and one relation with UserAccountEntity. What is confusing to me, is that the id of UserAccountEntity is generated in UserEntity. I wonder how I should define its id?
I created a class UserAccountActivityCode:
#Entity
#Table(name = "User_account_activity_code")
public class UserAccountActivityCode implements Serializable {
#Id
#GeneratedValue(generator = RandomAlphanumericIdGenerator.generatorName)
#GenericGenerator(name = RandomAlphanumericIdGenerator.generatorName, strategy = "com.myproject.project.core.utils.RandomAlphanumericIdGenerator")
private String id;
public UserAccountActivityCode() {}
}
But how should I refer to it in the UserAccountEntity class and where does UserAccountEntity get id from in that case?
Update 1:
I took out GeneratedValue out of UserAccountActivityCodeEntity, and added setUserId to set it when saving:
#Entity
#Table(name="user_account_activity_code_entity")
public class UserAccountActivityCodeEntity {
private final static int ACTIVITY_CODE_LENGTH = 6;
#Id
private String userId;
private String acitivityCode;
public UserAccountActivityCodeEntity(String id) {
this.acitivityCode = RandomStringUtils.randomAlphanumeric(ACTIVITY_CODE_LENGTH);
this.userId = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getAcitivityCode() {
return acitivityCode;
}
public void setAcitivityCode(String acitivityCode) {
this.acitivityCode = acitivityCode;
}
}
And I have the following code in my service layer:
userAccount = new UserAccountEntity(username,
passwordEncoder.encode(password));
userAccount.setUser(new UserEntity());
UserAccountActivityCodeEntity activityCode = new UserAccountActivityCodeEntity(userAccount.getId());
userAccount.setActivityCode(activityCode);
userAccountRepository.save(userAccount);
Which leads to this error:
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): com.myproject.project.core.entity.useraccount.UserAccountActivityCodeEntity
I'm doing a registration and I have the fields
Nome:
Data de Nascimento:
Inscrição Estadual:
Nome Responsável:
CPF Responsável:
Cep:
Bloco:
Número:
when i saving, I can not write data from the PessoasEnderecos class, the other data is recording normal. I'm getting all the data on the screen so much that I debugged the browser to see ..
It shows no error. Does anyone know what I'm missing ??
my class Pacientes
#Entity
#Table(name = "pacientes", schema = "sau")
public class Pacientes implements Serializable {
private static final long serialVersionUID = 5776384003601026304L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idPaciente")
private Long idPaciente;
#JoinColumn(name="idPessoa")
#ManyToOne(cascade = CascadeType.ALL)
private Pessoas pessoa;
#Column(name = "nomeResponsavel")
private String nomeResponsavel;
#Column(name = "cpfResponsavel")
private String cpfResponsavel;
public Pacientes() {
}
//gets and sets
}
my class Pessoas
#Entity
#Table(name = "pessoas", schema="glb")
public class Pessoas implements Serializable {
private static final long serialVersionUID = -4042023941980758267L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
private Long idPessoa;
#Temporal(TemporalType.DATE)
private Date dataNascimento;
private String inscricaoEstadual;
private String inscricaoMunicipal;
private String nome;
public Pessoas() {
}
//gets and sets
}
#Entity
#Table(name = "pessoas_enderecos" ,schema="glb")
public class PessoasEnderecos implements Serializable {
private static final long serialVersionUID = -2560542418318988673L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idPessoaEndereco;
private String bloco;
private String cep;
private String numero;
#JoinColumn(name="idPessoa")
#ManyToOne(optional = false, cascade = CascadeType.ALL)
private Pessoas pessoa;
public PessoasEnderecos() {
}
//gets and sets
}
my methods
class Controller
#RequestMapping(method = RequestMethod.POST, value = "/pacientes")
public Pacientes cadastrarPacientes(#RequestBody Pacientes pac) {
return pacientesService.cadastrar(pac);
}
class service
public Pacientes cadastrar(Pacientes pacientes){
return pacRepository.save(pacientes);
}
class repository
public interface PacientesRepository extends JpaRepository<Pacientes, Integer> {
}
You should also add the linkage #OneToMany in Pacientes:
public class Pacientes implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "pessoa")
#PrimaryKeyJoinColumn
private List<PessoasEnderecos> pessoasEnderecos = new ArrayList<>();
Update:
and your JSON should be something like this:
{
"nomeResponsavel": "abc",
"pessoasEnderecos": [
{
"bloco": "sdds",
"cep": "sdasdsad",
"numero": "sdasdsa"
}
]
}