In my Spring MVC/JPA application I have User and Department entities defined in respect to their database tables. As a rule many Users can belong to one Department, so I have the following in my User Class
#Entity(name = "USER")
public class User {
#Id
#Column(name = "Username")
private String username;
#ManyToOne
#JoinColumn(name = "Department")
private Department department;
}
When it comes time to save a new user I must associate the User with a Department's ID
User
username | BobbySurfs2020
departmentId | 43
Do I need to have an additional departmentId field defined on my User object just to make this association, or can I somehow use the embedded Department object to set this ID via my setter setDepartment()?
Update/Additional thought:
Is it even necessary to have Department nested in the User object like I have here? I'm thinking it would take up more memory to store a Department for every user I fetch than it would to associate only the Department ID's. That way if I needed a departments info at any time I could just simply look them up by the ID associated to the user. Thoughts?
Thanks much!
Do I need to have an additional departmentId field defined on my User object just to make this association.
No
Is it even necessary to have Department nested in the User object like I have here?
Yes. You are using an Object Relational Mapper so map objects.
In the MVC world you can easily handle this by means of a converter which will take the ID passed from the UI, look up the relevant entity and set this on the Entity being edited.
#Component
public class StringToDepartmentConverter implements Converter<String, Department> {
#Autowired
private DepartmentService service;
/**
*departmentId is the HTTP post param passed in by the framework
*/
public Department convert(String departmentId ) {
return service.findDepartment(Integer.parseInt(departmentId ));
}
}
Register this with the framework:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "..." })
public class MvcConfiguration extends WebMvcConfigurerAdapter{
//other configuration
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
formatterRegistry.addConverter(departmentConverter());
}
#Bean
public StringToDepartmentConverter departmentConverter(){
return new StringToDepartmentConverter ();
}
}
Related
I've using spring data JPA repositories to save the data into my tables.
In one of the situation, I've to store data in two different tables. These two tables have the exact same schema. But I can only store in one table as JpaRepository maps to just one table.
Example:
#Entity
#Table(name="users")
class User {
private Long userId;
private String firstName;
private String lastName;
}
interface MyRepo1 extends JpaRepositories<User,Long> {
}
interface MyRepo2 extends JpaRepositories<User,Long> {
}
Is there a way to map a single entity into multiple JpaRepositories? So, when I call MyRepo1.save(user) it saves in Users table and when I call MyRepo2.save(user), it saves in BackupUsers(exact same schema as Users) table.
Thanks
You should have two different entities, for instance, User entity AppUser entity, each one of them pointing to the different tables like this, assuming that the two table names are users and app_users:
#Entity
#Table(name="users")
public class User extends SuperUser {...}
and
#Entity
#Table(name="app_users")
public class AppUser extends SuperClass {...}
Of course, to avoid code duplication you can put your table fields in a superclass named SuperClass, for example:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class SuperUser {
#Id
private Long userId;
private String firstName;
private String lastName;
// getters and setters, constructors
}
After, you need to create two repositories for each entity class:
public interface UserRepository extends JpaRepositories<User, Long> {
}
public interface AppUserRepository extends JpaRepositories<AppUser,Long> {
}
NOTE: To finish, it's not recommended at all to have two tables that hold the same fields.
I don't think you can save in different tables with two different repositories, because you can't specify the table name in repository, but in the entity, I would suggest to use :
// class which hold common fields
class User {
private Long userId;
private String firstName;
private String lastName;
}
// entity for table 1
#Entity
#Table(name="table1")
class User1 extends User {}
// entity for table 1
#Entity
#Table(name="table2")
class User2 extends User {}
// repo for entity 1
interface MyRepo1 extends JpaRepositories<User1,Long> {}
// repo for entity 2
interface MyRepo2 extends JpaRepositories<User2 ,Long> {}
I'm working on a professional social network application using couchbase server 4.1 with spring data couchbase 2.2.4 in a spring boot application using java 1.8.
I want to replace the next stream which searches a LikeEntity with specific company in the database by a N1QL based query which searches a user with the same previous likeEntity unnested:
Here is the service containing the stream to replace by the query:
#Service
class CompanyServiceImpl implements CompanyService{
#Override
public void addCompanyToLike(UserEntity user, Company company){
int incrementValue=1;
LikeEntity existingLike=user.getLikeEntities()
.stream()
.filter(le->le.getCompany().getName().equals(company.getName()))
.findFirst();
//rest of process
}
}
Here is the different java beans you will need to look at:
UserEntity class:
#Document
#ViewIndexed(designDoc = "user")
class UserEntity implements Serializable{
#Field private String id;
#Reference
private List<LikeEntity> likeEntities=new ArrayList<LikeEntity>();
//Other attributes plus getters and setters:
}
LikeEntity class:
#Document
class LikeEntity implements serializable{
#Reference
private Company company;
#Reference
private Job job;
// other attributes plus getters and setters
}
As you see above, the LikeEntity class may contain any object liked by the user, it can be a company, a job or another object. Also the LikeEntity is stored only inside a user document as element of user's list of likes and not independately in the database.It's my choice of modelization because the LikeEntity won't by used in other java beans of my application
Company:
#Document
#ViewIndexed(designDoc = "company")
class Company implements Serializable{
#Field private String id;
#Field private String name;
//Other attributes plus getters and setters
}
N.B: I've tried the next query inside UserRepository but it didn't work:
UserRepository:
#Repository
#N1qlPrimaryIndexed
#N1qlSecondaryIndexed(indexName = "user")
public interface UserRepository extends CouchbaseRepository<UserEntity, String> {
#Query("SELECT b.*, likeEntity FROM #{#n1ql.bucket} AS b UNNEST b.likeEntities AS likeEntity WHERE b.id=$1 AND likeEntity.company.name=$2")
UserEntity findByLikedCompanyName(String idUser
, String companyName);
}
I'm looking for your answers, and thank you so much in advance.
I am experimenting with jpa and hibernate relations. I'm using a table named users and a table named emails. A user can have many emails.
When I run my Spring boot application along with the following code I get one email record in my h2 database. The email_address of this record is testAddress and the user_username column of this record is null. The user_username column is listed as a foreign key on the emails table. My question is, why is emailRepository.save(email1) successful when there is no corresponding user in the database?
#Entity
#Table(name = "emails")
public class Email {
#Id
private String emailAddress;
#ManyToOne
private User user;
...
}
#Entity
#Table(name = "users")
public class User {
#Id
private String username;
#OneToMany(mappedBy="user", cascade=CascadeType.ALL, orphanRemoval=true)
private Set<Email> emails;
...
}
public interface UserRepository extends JpaRepository<User, String> {
}
public interface EmailRepository extends JpaRepository<Email, String> {
}
#Component
public class UserRepositoryCommandLineRunner implements CommandLineRunner {
#Autowired
private EmailRepository emailRepository;
public void run(String... args) throws Exception {
Email email1 = new Email();
email1.setEmailAddress("testAddress");
emailRepository.save(email1);
}
}
Take a look at the documentation of the JoinColumn annotation:
https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/JoinColumn.html#nullable()
It is mentioned:
If the JoinColumn annotation itself is defaulted, a single join column
is assumed and the default values apply.
Since you did not specify a JoinColumn in your ManyToOne mapping, then Hibernate would assume the default JoinColumn. If you take a look at the JoinColumn.nullable attribute, it is defaulted to true. Thus, when Hibernate generates your schema, the foreign key column is by default NULLABLE.
You may need to explicitly add a #JoinColumn annotation on top of your #ManyToOne mapping and set its nullable attribute to false.
#ManyToOne
#JoinColumn(nullable=false)
private User user;
This way, it'll throw out an error when you try to insert email without a user.
I have one simple class
#Entity
#Table(name="user")
public class User implements Serializable {
#Id
#GeneratedValue
private Integer Id;
#Length(min = 5, message = "Username must be at least 5 characters long.")
#Column(name="username",nullable=false,unique=true)
private String userName;
#ManyToMany(cascade= {CascadeType.PERSIST},fetch=FetchType.EAGER)
#JoinTable(name="user_user_profile")
private Set<UserProfile> userProfile = new HashSet<>();
}
And second class:
#Entity
#Table(name = "user_profile")
public class UserProfile {
#javax.persistence.Id
#GeneratedValue
private int Id;
#Column(name = "type", nullable = false, unique = true)
#Enumerated(EnumType.STRING)
private UserProfileType type = UserProfileType.USER;
}
public enum UserProfileType {
USER("USER"),
ADMIN("ADMIN");
}
I'm using Spring MVC and Spring Secuirty with Hibernate. Is there any way to on start of the app make every possible entry in UserProfile Entity (there is only two)? Do I have to get UserProfile from database (via TypedQuery or EntityManager.find() ) and then add it to the User to not make any exceptions?
The enum items are static in your application, so I wouldn't try to make automatic changes in the database. Adding a new record is trivial, but removing an item that is already referenced may need individual care. These values are essential for your application, so I think they should be included in your SQL scripts.
If you are using DB versioning tools such as Flyway or Liquibase, add/remove records of the user_profile table in the migration scripts. They can be configured to run the migrations before your application (and Hibernate) starts, so the application will always see the correct data.
You can add a application start up event and persist the user profiles. You can delete all the user profiles before the application shut down as well. But I wouldn't recommend this as I assume the UserProfiles wouldn't change frequently. If that is the case, you are better off preloading the user profiles via some sql script as suggested in the other answer. If you really want to do it via app, the safest way would be to delete before the app gets shut down. Following is the sample snippet. I assume you are using spring-data-jpa and provided the snippet.
#Component
public class AppStartedListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private UserProfileRepository repository;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
for(UserProfileType userProfileType: UserProfileType.values()) {
UserProfile up = new UserProfile(userProfileType);
repository.save(up);
}
}
}
#Component
public class AppStoppedListener implements ApplicationListener<ContextClosedEvent> {
#Autowired
private UserProfileRepository repository;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repository.deleteAll();
}
}
public interface UserProfileRepository extends CrudRepository<UserProfile, Integer> {
}
So I added method to dao layer:
#Transactional
#EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
UserProfile user=new UserProfile();
em.persist(user);
UserProfile admin=new UserProfile();
admin.setType(UserProfileType.ADMIN);
em.persist(admin);
}
And now, before adding new User i just use HQL to get persistent UserProfile object that I can add to my User. Altough it works I will probably try to load it from some sort of *.sql file since I had to add method metioned above to the Dao layer interface (because of interface type proxy) and I don't like it to be honest.
I have the following simple scenario:
Cars table: Id, Name, Model
Cars Schedule table:Id, CarId, ScheduleTime, ScheduleDate
I am using Spring MVC with Hibernate, with the structure of:
Domain
repo
repoImpl
service
serviceImpl
what I need to do is displaying the car name in the list of the Cars Schedule without having to add a field called CarName in the CarsSchedule table.
What is the best practice for doing this?
In Car entity you should have
#OneToMany(mappedBy = "car")
private List<CarsSchedule> schedules;
I assumed that the relation is #OneToMany, but you can just switch it to #OneToOne private CarsSchedule schedule;, if that's the case.
And in CarsSchedule entity, this
#ManyToOne
#JoinColumn(name = "CarId")
private Car car;
With this setup, once you have the carsSchedule instance in your controller (and model), you can display the name of the car on the page with #{carsSchedule.car.name}.
I think you should have a one-to-many relationship in the table Cars Schedule:
//in class CarsSchedule
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CAR_ID", nullable = false)
public Stock getCar() {
return this.car;
}
Then in the controller, you retrieve the list of CarSchedule (that has also the Car into it) and put the list into the model:
#RequestMapping(value = "car-list", method = RequestMethod.GET)
public String getList(HttpSession session, Map<String, Object> model) {
//get the list from the service
List<CarsSchedule> list = service.getCarScheduleList();
//put into the model
model.put("form", new PersonUserRoleForm());
return "mymodel";
}
then you have a mymodel.jsp maybe, where you can retieve the variable mymodel
You can follow this simple tutorial:
http://www.mkyong.com/hibernate/hibernate-one-to-many-relationship-example-annotation/
Ciao
If you're setting the annotations on the property accessor method, you can simply add
#Transient
public String getName() {
return car.getName();
}
It will be invisible for your database but visible for all other layers. In JSP you would access it as ${carsSchedule.name}, and it would be an immediate child in your JSON or XML if that is the representation you use
Even if your annotating properties themselves you can still do
#Transient
private String name;
public String getName() {
return car.getName();
}
I recon that your main idea is to avoid persisting another field. Note just that the transient annotation is the javax.persistence.Transient and it has no implication to the serialization just tells the persistence provider to ignore the field upon persisting